diff --git a/frameworks/native/backup_ext/include/ext_extension.h b/frameworks/native/backup_ext/include/ext_extension.h index c39e05fcd9dbdfcc9d29cc48be36a806f5c75ec2..ae350d219fa753d0e344b6f55c8dcb9df935d776 100644 --- a/frameworks/native/backup_ext/include/ext_extension.h +++ b/frameworks/native/backup_ext/include/ext_extension.h @@ -35,6 +35,9 @@ public: ErrCode HandleBackup() override; ErrCode HandleRestore() override; + ErrCode GetIncrementalFileHandle(const std::string &fileName); + ErrCode PublishIncrementalFile(const std::string &fileName); + void AsyncTaskRestoreForUpgrade(void); public: @@ -68,6 +71,13 @@ private: */ int DoRestore(const string &fileName); + /** + * @brief incremental restore + * + * @param fileName name of the file that to be untar + */ + int DoIncrementalRestore(const string &fileName); + /** @brief clear backup restore data */ void DoClear(); @@ -93,6 +103,12 @@ private: */ void AsyncTaskRestore(); + /** + * @brief Executing Incremental Restoration Tasks Asynchronously + * + */ + void AsyncTaskIncrementalRestore(); + void AsyncTaskOnBackup(); private: diff --git a/frameworks/native/backup_ext/include/untar_file.h b/frameworks/native/backup_ext/include/untar_file.h index 1f92f88a2094670638d140ef5f511b49788ff36e..fe0b12714502216df0e16aba5145f873c2b4eb63 100644 --- a/frameworks/native/backup_ext/include/untar_file.h +++ b/frameworks/native/backup_ext/include/untar_file.h @@ -32,6 +32,8 @@ public: typedef enum { ERR_FORMAT = -1 } ErrorCode; static UntarFile &GetInstance(); int UnPacket(const std::string &tarFile, const std::string &rootPath); + int IncrementalUnPacket(const std::string &tarFile, const std::string &rootPath, + const std::unordered_map &includes); private: UntarFile() = default; @@ -46,6 +48,13 @@ private: */ int ParseTarFile(const std::string &rootPath); + /** + * @brief parse incremental tar file + * + * @param rootpath 解包的目标路径 + */ + int ParseIncrementalTarFile(const std::string &rootPath); + /** * @brief verfy check sum * @@ -112,6 +121,15 @@ private: */ void ParseFileByTypeFlag(char typeFlag, bool &isSkip, FileStatInfo &info); + /** + * @brief parse incremental file by typeFlag + * + * @param typeFlag 文件类型标志 + * @param isSkip 是否跳过当前文件 + * @param info 文件属性结构体 + */ + void ParseIncrementalFileByTypeFlag(char typeFlag, bool &isSkip, FileStatInfo &info); + /** * @brief Handle file ownership groups * @@ -128,6 +146,7 @@ private: off_t tarFileBlockCnt_ {0}; off_t pos_ {0}; size_t readCnt_ {0}; + std::unordered_map includes_; }; } // namespace OHOS::FileManagement::Backup diff --git a/frameworks/native/backup_ext/src/ext_extension.cpp b/frameworks/native/backup_ext/src/ext_extension.cpp index 083e4d7d442e3717b74e3b3d54af96e42b9de4b8..c0a294dc9007904fd9abd1154e9270c9f06f0bf5 100644 --- a/frameworks/native/backup_ext/src/ext_extension.cpp +++ b/frameworks/native/backup_ext/src/ext_extension.cpp @@ -38,6 +38,7 @@ #include "b_filesystem/b_file.h" #include "b_json/b_json_cached_entity.h" #include "b_json/b_json_entity_ext_manage.h" +#include "b_json/b_report_entity.h" #include "b_resources/b_constants.h" #include "b_tarball/b_tarball_factory.h" #include "filemgmt_libhilog.h" @@ -126,6 +127,76 @@ UniqueFd BackupExtExtension::GetFileHandle(const string &fileName) } } +static string ChangeFileExtension(const string &fileName, const string &fileExt) +{ + auto n = fileName.rfind("."); + if (n == string::npos) { + throw BError(BError::Codes::EXT_INVAL_ARG, "filename has no extension"); + } + + return string(fileName).replace(n, fileName.length()-n, "." + fileExt); +} + +static string GetReportFileName(const string &fileName) +{ + string reportName; + if (fileName.rfind(".") != string::npos) { + reportName = ChangeFileExtension(fileName, string(BConstants::REPORT_FILE_EXT)); + } else { + reportName = fileName + "." + string(BConstants::REPORT_FILE_EXT); + } + + return reportName; +} + +ErrCode BackupExtExtension::GetIncrementalFileHandle(const string &fileName) +{ + try { + if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) { + HILOGI("Failed to get file handle, because action is %{public}d invalid", extension_->GetExtensionAction()); + throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid"); + } + + VerifyCaller(); + + string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); + if (mkdir(path.data(), S_IRWXU) && errno != EEXIST) { + string str = string("Failed to create restore folder. ").append(std::generic_category().message(errno)); + throw BError(BError::Codes::EXT_INVAL_ARG, str); + } + + string tarName = path + fileName; + if (fileName.find('/') != string::npos) { + HILOGD("GetFileHandle: fileName include path symbol, need to make hash."); + tarName = path + GenerateHashForFileName(fileName); + if (CheckIfTarSuffix(fileName)) { + tarName += ".tar"; + } + } + if (access(tarName.c_str(), F_OK) == 0) { + throw BError(BError::Codes::EXT_INVAL_ARG, string("The file already exists")); + } + UniqueFd fd = UniqueFd(open(tarName.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)); + + // TODO + + // 对应的简报文件 + string reportName = GetReportFileName(tarName); + if (access(reportName.c_str(), F_OK) == 0) { + throw BError(BError::Codes::EXT_INVAL_ARG, string("The report file already exists")); + } + UniqueFd reportFd = UniqueFd(open(reportName.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)); + + // TODO + + return ERR_OK; + } catch (...) { + HILOGE("Failed to get incremental file handle"); + DoClear(); + return BError(BError::Codes::EXT_INVAL_ARG).GetCode(); + } +} + ErrCode BackupExtExtension::HandleClear() { HILOGI("begin clear"); @@ -263,6 +334,57 @@ ErrCode BackupExtExtension::PublishFile(const string &fileName) } } +ErrCode BackupExtExtension::PublishIncrementalFile(const string &fileName) +{ + HILOGE("begin publish incremental file. fileName is %{public}s", fileName.data()); + try { + if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) { + throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid"); + } + VerifyCaller(); + + string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); + string tarName = path + fileName; + // 带路径的文件名在这里转换为hash值 + if (fileName.find('/') != string::npos) { + HILOGD("PublishFile: fileName include path symbol, need to make hash."); + tarName = path + GenerateHashForFileName(fileName); + if (CheckIfTarSuffix(fileName)) { + tarName += ".tar"; + } + } + { + BExcepUltils::VerifyPath(tarName, true); + unique_lock lock(lock_); + if (find(tars_.begin(), tars_.end(), fileName) != tars_.end() || access(tarName.data(), F_OK) != 0) { + throw BError(BError::Codes::EXT_INVAL_ARG, "The file does not exist"); + } + tars_.push_back(fileName); + if (!IsAllFileReceived(tars_)) { + return ERR_OK; + } + } + + // 异步执行解压操作 + if (extension_->AllowToBackupRestore()) { + AsyncTaskIncrementalRestore(); + } + + return ERR_OK; + } catch (const BError &e) { + DoClear(); + return e.GetCode(); + } catch (const exception &e) { + HILOGE("Catched an unexpected low-level exception %{public}s", e.what()); + DoClear(); + return BError(BError::Codes::EXT_BROKEN_FRAMEWORK).GetCode(); + } catch (...) { + HILOGE("Unexpected exception"); + DoClear(); + return BError(BError::Codes::EXT_BROKEN_FRAMEWORK).GetCode(); + } +} + ErrCode BackupExtExtension::HandleBackup() { string usrConfig = extension_->GetUsrConfig(); @@ -414,6 +536,65 @@ int BackupExtExtension::DoRestore(const string &fileName) return ERR_OK; } +static unordered_map GetTarIncludes(const string &tarName) +{ + // 根据简报文件获取待解压的文件列表,如果简报文件内容为空,则进行全量解压,返回空 + unordered_map includes; + + // 获取简报文件内容 TODO + string reportName = GetReportFileName(tarName); + + // 获取简报内容 + BReportEntity rp(UniqueFd(open(reportName.data(), O_RDONLY))); + vector infos = rp.GetReportFileInfos(); + for (auto iter : infos) { + includes.emplace(iter.filePath, true); + } + + return includes; +} + +int BackupExtExtension::DoIncrementalRestore(const string &fileName) +{ + HILOGI("Do incremental restore"); + if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) { + return EPERM; + } + // REM: 给定version + // REM: 解压启动Extension时即挂载好的备份目录中的数据 + string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); + string tarName = path + fileName; + + // 带路径的恢复需要找到hash后的待解压文件 + if (fileName.find('/') != string::npos) { + HILOGD("DoIncrementalRestore: fileName include path symbol, need to make hash."); + string filePath = path + GenerateHashForFileName(fileName); + size_t pos = filePath.rfind('/'); + if (pos == string::npos) { + return EPERM; + } + string folderPath = filePath.substr(0, pos); + if (!ForceCreateDirectory(folderPath.data())) { + HILOGE("Failed to create directory"); + return EPERM; + } + tarName = filePath; + if (CheckIfTarSuffix(fileName)) { + tarName += ".tar"; + } + } + + // 当用户指定fullBackupOnly字段或指定版本的恢复,解压目录当前在/backup/restore + if (extension_->SpeicalVersionForCloneAndCloud() || extension_->UseFullBackupOnly()) { + UntarFile::GetInstance().IncrementalUnPacket(tarName, path, GetTarIncludes(tarName)); + } else { + UntarFile::GetInstance().IncrementalUnPacket(tarName, "/", GetTarIncludes(tarName)); + } + HILOGI("Application recovered successfully, package path is %{public}s", tarName.c_str()); + + return ERR_OK; +} + void BackupExtExtension::AsyncTaskBackup(const string config) { auto task = [obj {wptr(this)}, config]() { @@ -611,6 +792,36 @@ static void DeleteBackupTars() } } +static void DeleteBackupIncrementalTars() +{ + // The directory include tars and manage.json which would be deleted + BJsonCachedEntity cachedEntity(UniqueFd(open(INDEX_FILE_RESTORE.data(), O_RDONLY))); + auto cache = cachedEntity.Structuralize(); + auto info = cache.GetExtManage(); + auto path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); + for (auto &item : info) { + if (ExtractFileExt(item) != "tar" || IsUserTar(item, INDEX_FILE_RESTORE)) { + continue; + } + string tarPath = path + item; + if (!RemoveFile(tarPath)) { + HILOGE("Failed to delete the backup tar %{public}s", tarPath.c_str()); + } + // 删除简报文件 + string reportPath = GetReportFileName(tarPath); + if (!RemoveFile(reportPath)) { + HILOGE("Failed to delete the backup report"); + } + } + if (!RemoveFile(INDEX_FILE_RESTORE)) { + HILOGE("Failed to delete the backup index %{public}s", INDEX_FILE_RESTORE.c_str()); + } + string reportManagePath = GetReportFileName(INDEX_FILE_RESTORE); // GetIncrementalFileHandle创建的空fd + if (!RemoveFile(reportManagePath)) { + HILOGE("Failed to delete the backup report index %{public}s", reportManagePath.c_str()); + } +} + void BackupExtExtension::AsyncTaskRestore() { auto task = [obj {wptr(this)}, tars {tars_}]() { @@ -664,6 +875,59 @@ void BackupExtExtension::AsyncTaskRestore() }); } +void BackupExtExtension::AsyncTaskIncrementalRestore() +{ + auto task = [obj {wptr(this)}, tars {tars_}]() { + auto ptr = obj.promote(); + BExcepUltils::BAssert(ptr, BError::Codes::EXT_BROKEN_FRAMEWORK, + "Ext extension handle have been already released"); + try { + // 解压 + int ret = ERR_OK; + for (auto item : tars) { // 处理要解压的tar文件 + if (ExtractFileExt(item) == "tar" && !IsUserTar(item, INDEX_FILE_RESTORE)) { + ret = ptr->DoIncrementalRestore(item); + } + } + // 恢复用户tar包以及大文件 + // 目的地址是否需要拼接path(临时目录),FullBackupOnly为true并且非特殊场景 + bool appendTargetPath = ptr->extension_->UseFullBackupOnly() && + !ptr->extension_->SpeicalVersionForCloneAndCloud(); + RestoreBigFiles(appendTargetPath); + + // delete 1.tar/manage.json + DeleteBackupIncrementalTars(); + + if (ret == ERR_OK) { + HILOGI("after extra, do incremental restore."); + ptr->AsyncTaskRestoreForUpgrade(); + } else { + ptr->AppDone(ret); + ptr->DoClear(); + } + } catch (const BError &e) { + ptr->AppDone(e.GetCode()); + } catch (const exception &e) { + HILOGE("Catched an unexpected low-level exception %{public}s", e.what()); + ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode()); + } catch (...) { + HILOGE("Failed to restore the ext bundle"); + ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode()); + } + }; + + // REM: 这里异步化了,需要做并发控制 + // 在往线程池中投入任务之前将需要的数据拷贝副本到参数中,保证不发生读写竞争, + // 由于拷贝参数时尚运行在主线程中,故在参数拷贝过程中是线程安全的。 + threadPool_.AddTask([task]() { + try { + task(); + } catch (...) { + HILOGE("Failed to add task to thread pool"); + } + }); +} + void BackupExtExtension::AsyncTaskRestoreForUpgrade() { auto task = [obj {wptr(this)}]() { diff --git a/frameworks/native/backup_ext/src/untar_file.cpp b/frameworks/native/backup_ext/src/untar_file.cpp index 6e8ff73f2fd4a9438110061fcf8f4a7a9657d785..1a3a6d9dec76f43bd7cd239e3ac5df1483d2d679 100644 --- a/frameworks/native/backup_ext/src/untar_file.cpp +++ b/frameworks/native/backup_ext/src/untar_file.cpp @@ -79,6 +79,26 @@ int UntarFile::UnPacket(const string &tarFile, const string &rootPath) return 0; } +int UntarFile::IncrementalUnPacket(const string &tarFile, const string &rootPath, + const unordered_map &includes) +{ + includes_ = includes; + tarFilePtr_ = fopen(tarFile.c_str(), "rb"); + if (tarFilePtr_ == nullptr) { + HILOGE("Failed to open tar file %{public}s, err = %{public}d", tarFile.c_str(), errno); + return errno; + } + + if (ParseIncrementalTarFile(rootPath) != 0) { + HILOGE("Failed to parse tar file"); + } + + fclose(tarFilePtr_); + tarFilePtr_ = nullptr; + + return 0; +} + void UntarFile::HandleTarBuffer(const string &buff, const string &name, FileStatInfo &info) { info.mode = static_cast(ParseOctalStr(&buff[0] + TMODE_BASE, TMODE_LEN)); @@ -147,6 +167,56 @@ int UntarFile::ParseTarFile(const string &rootPath) return ret; } +int UntarFile::ParseIncrementalTarFile(const string &rootPath) +{ + // re-parse tar header + rootPath_ = rootPath; + char buff[BLOCK_SIZE] = {0}; + bool isSkip = false; + FileStatInfo info {}; + + // tarFileSize + int ret = fseeko(tarFilePtr_, 0L, SEEK_END); + tarFileSize_ = ftello(tarFilePtr_); + // reback file to begin + ret = fseeko(tarFilePtr_, 0L, SEEK_SET); + + while (1) { + readCnt_ = fread(buff, 1, BLOCK_SIZE, tarFilePtr_); + if (readCnt_ < BLOCK_SIZE) { + HILOGE("Parsing tar file completed, read data count is less then block size."); + return 0; + } + // two empty continuous block indicate end of file + if (IsEmptyBlock(buff)) { + char tailBuff[BLOCK_SIZE] = {0}; + size_t tailRead = fread(tailBuff, 1, BLOCK_SIZE, tarFilePtr_); + if (tailRead == BLOCK_SIZE && IsEmptyBlock(tailBuff)) { + HILOGE("Parsing tar file completed, tailBuff is empty."); + return 0; + } + } + // check header + TarHeader *header = reinterpret_cast(buff); + if (!IsValidTarBlock(*header)) { + // when split unpack, ftell size is over than file really size [0,READ_BUFF_SIZE] + if (ftello(tarFilePtr_) > (tarFileSize_ + READ_BUFF_SIZE) || !IsEmptyBlock(buff)) { + HILOGE("Invalid tar file format"); + ret = ERR_FORMAT; + } + return ret; + } + HandleTarBuffer(string(buff, BLOCK_SIZE), header->name, info); + ParseIncrementalFileByTypeFlag(header->typeFlag, isSkip, info); + ret = HandleFileProperties(isSkip, info); + if (ret != 0) { + HILOGE("Failed to handle file property"); + } + } + + return ret; +} + void UntarFile::ParseFileByTypeFlag(char typeFlag, bool &isSkip, FileStatInfo &info) { switch (typeFlag) { @@ -179,6 +249,43 @@ void UntarFile::ParseFileByTypeFlag(char typeFlag, bool &isSkip, FileStatInfo &i } } +void UntarFile::ParseIncrementalFileByTypeFlag(char typeFlag, bool &isSkip, FileStatInfo &info) +{ + switch (typeFlag) { + case REGTYPE: + case AREGTYPE: + if (!includes_.empty() && includes_.find(info.fullPath) == includes_.end()) { // not in includes + isSkip = true; + fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET); + break; + } + ParseRegularFile(info, typeFlag, isSkip); + break; + case SYMTYPE: + isSkip = false; + break; + case DIRTYPE: + CreateDir(info.fullPath, info.mode); + isSkip = false; + break; + case GNUTYPE_LONGNAME: { + size_t nameLen = static_cast(tarFileSize_); + if (nameLen < PATH_MAX_LEN) { + fread(&(info.longName[0]), sizeof(char), nameLen, tarFilePtr_); + } + isSkip = true; + fseeko(tarFilePtr_, pos_ + tarFileBlockCnt_ * BLOCK_SIZE, SEEK_SET); + break; + } + default: { + // Ignoring, skip + isSkip = true; + fseeko(tarFilePtr_, tarFileBlockCnt_ * BLOCK_SIZE, SEEK_CUR); + break; + } + } +} + void UntarFile::ParseRegularFile(FileStatInfo &info, char typeFlag, bool &isSkip) { FILE *destFile = CreateFile(info.fullPath, info.mode, typeFlag); diff --git a/utils/include/b_json/b_report_entity.h b/utils/include/b_json/b_report_entity.h new file mode 100644 index 0000000000000000000000000000000000000000..50fb220e423ed78d5913237d2dae4e1d76f1d9a3 --- /dev/null +++ b/utils/include/b_json/b_report_entity.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2022-2023 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 OHOS_FILEMGMT_BACKUP_B_REPORT_ENTITY_H +#define OHOS_FILEMGMT_BACKUP_B_REPORT_ENTITY_H + +#include +#include + +namespace OHOS::FileManagement::Backup { +struct ReportFileInfo { + string filePath {""}; + mode_t mode {0}; + bool isDir {false}; + off_t size {0}; + off_t mtime {0}; + string hash {""}; +}; +class BReportEntity { +public: + ErrCode ParseReportFileInfo(ReportFileInfo &info, const std::vector &attrs) + { + try { + if (attrs[0].empty()) { + HILOGE("Invalid filePath:%{public}s", attrs[0].c_str()); + return EPERM; + } + info.filePath = attrs[0]; + + if (!attrs[1].empty()) { + info.mode = static_cast(atoi(attrs[1].c_str())); + } + + if (attrs[2].empty()) { + HILOGE("Invalid isDir:%{public}s", attrs[2].c_str()); + return EPERM; + } + info.isDir = atoi(attrs[2].c_str()) == 1 ? true : false; + + if (!attrs[3].empty()) { + info.size = static_cast(atol(attrs[3].c_str())); + } + + if (!attrs[4].empty()) { + info.mtime = static_cast(atol(attrs[4].c_str())); + } + + info.hash = attrs[5]; + + return ERR_OK; + } catch (...) { + HILOGE("Failed to ParseReportFileInfo"); + return EPERM; + } + } + + ErrCode ParseInfoLine(const std::string &line) + { + try { + std::stringstream lineStream = std::stringstream(line); + std::stringstream infoStream; + std::string info = {}; + std::string key = {}; + std::vector keys = {}; + while (!lineStream.eof()) { + getline(lineStream, info, infoSep_); + infoStream = std::stringstream(info); + keys.clear(); + while (!infoStream.eof()) { + getline(infoStream, key, infoAlign_); + if (!key.empty()) { + keys.push_back(key); + } + } + if (keys.size() != 2) { + HILOGE("Failed to Parse info:%{public}s", info.c_str()); + continue; + } + if (keys[0] == VERSION_TAG_) { + version = keys[1]; + } else if (keys[0] == ATTR_NUM_TAG_) { + attrNum = atoi(keys[1].c_str()); + } else { + HILOGE("invalid info:%{public}s", info.c_str()); + } + } + + return ERR_OK; + } catch (...) { + HILOGE("Failed to ParseInfoLine"); + return EPERM; + } + } + + /** + * @brief 获取Report信息 + * + * @return std::vector + */ + std::vector GetReportFileInfos() + { + std::vector infos {}; + + std::unique_ptr rawBuf = BFile::ReadFile(srcFile_); + std::stringstream fileStream(rawBuf.get()); + std::string line; + std::stringstream lineStream; + std::string attr = {}; + std::vector attrs; + bool infoLine = false; + bool attrLine = false; + while (!fileStream.eof()) { + getline(fileStream, line, lineSep_); + + if (!line.empty()) { + if (!infoLine) { + infoLine = true; + ParseInfoLine(line); + continue; + } + if (!attrLine) { + attrLine = true; + continue; + } + + lineStream = std::stringstream(line); + attrs.clear(); + while (!lineStream.eof()) { + getline(lineStream, attr, attrSep_); + attrs.emplace_back(attr); + } + if (attrs.size() != attrNum) { + HILOGE("Invalid line:%{public}s", line.c_str()); + continue; + } + ReportFileInfo info = {}; + auto code = ParseReportFileInfo(info, attrs); + if (code != ERR_OK) { + HILOGE("ParseReportFileInfo err:%{public}d %{public}s", code, line.c_str()); + continue; + } else { + infos.emplace_back(info); + } + } + } + + return infos; + } + +public: + /** + * @brief 构造方法 + * + * @param fd + */ + explicit BReportEntity(UniqueFd fd) : srcFile_(std::move(fd)) {} + + BReportEntity() = delete; + virtual ~BReportEntity() = default; + +public: + string version = ""; + unsigned int attrNum = 0; + +protected: + UniqueFd srcFile_; + const char lineSep_ = '\n'; + const char attrSep_ = ';'; + const char infoSep_ = '&'; + const char infoAlign_ = '='; + const string VERSION_TAG_ = "version"; + const string ATTR_NUM_TAG_ = "attrNum"; +}; +} // namespace OHOS::FileManagement::Backup + +#endif // OHOS_FILEMGMT_BACKUP_B_REPORT_ENTITY_H \ No newline at end of file diff --git a/utils/include/b_resources/b_constants.h b/utils/include/b_resources/b_constants.h index 9f4ae877b689f5fc4318fb3a35a792b5c087b3fd..18d3c899155c777347f025159c18cfd25b9d88d2 100644 --- a/utils/include/b_resources/b_constants.h +++ b/utils/include/b_resources/b_constants.h @@ -116,6 +116,9 @@ static inline std::string_view EXT_BACKUP_MANAGE = "manage.json"; // 包管理元数据配置文件 static inline std::string_view BACKUP_CONFIG_JSON = "backup_config.json"; +// 简报文件名后缀 +static inline std::string_view REPORT_FILE_EXT = "rp"; + // 特殊版本信息 constexpr int DEFAULT_VERSION_CODE = 0; static inline std::string_view DEFAULT_VERSION_NAME = "0.0.0.0";