From fb38f33a9235783903a697415e0dd3b338c71826 Mon Sep 17 00:00:00 2001 From: huaqingsimeng Date: Thu, 18 Jan 2024 09:35:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Ebackup=5Ftool=E5=A2=9E?= =?UTF-8?q?=E9=87=8F=E5=A4=87=E4=BB=BD=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: huaqingsimeng --- .../include/tools_op_incremental_backup.h | 24 ++ .../src/tools_op_incremental_backup.cpp | 362 ++++++++++++++++++ 2 files changed, 386 insertions(+) create mode 100644 tools/backup_tool/include/tools_op_incremental_backup.h create mode 100644 tools/backup_tool/src/tools_op_incremental_backup.cpp diff --git a/tools/backup_tool/include/tools_op_incremental_backup.h b/tools/backup_tool/include/tools_op_incremental_backup.h new file mode 100644 index 000000000..66ce9448a --- /dev/null +++ b/tools/backup_tool/include/tools_op_incremental_backup.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 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_TOOLS_OP_INCREMENTAL_BACKUP_H +#define OHOS_FILEMGMT_BACKUP_TOOLS_OP_INCREMENTAL_BACKUP_H + +namespace OHOS::FileManagement::Backup { + bool IncrementalBackUpRegister(); + +} // namespace OHOS::FileManagement::Backup + +#endif // OHOS_FILEMGMT_BACKUP_TOOLS_OP_INCREMENTAL_BACKUP_H \ No newline at end of file diff --git a/tools/backup_tool/src/tools_op_incremental_backup.cpp b/tools/backup_tool/src/tools_op_incremental_backup.cpp new file mode 100644 index 000000000..2da9b362e --- /dev/null +++ b/tools/backup_tool/src/tools_op_incremental_backup.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2024 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "b_error/b_error.h" +#include "b_filesystem/b_file.h" +#include "b_json/b_json_entity_ext_manage.h" +#include "b_resources/b_constants.h" +#include "backup_kit_inner.h" +#include "base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter/hitrace_meter.h" +#include "service_proxy.h" +#include "tools_op.h" +#include "tools_op_incremental_backup.h" + +namespace OHOS::FileManagement::Backup { +using namespace std; + +class SessionBckup { +public: + void UpdateBundleReceivedFiles(const BundleName &bundleName, const string &fileName) + { + lock_guard lk(lock_); + bundleStatusMap_[bundleName].receivedFile.insert(fileName); + TryClearBundleOfMap(bundleName); + } + + void SetIndexFiles(const BundleName &bundleName, UniqueFd fd) + { + BJsonCachedEntity cachedEntity(move(fd)); + auto cache = cachedEntity.Structuralize(); + lock_guard lk(lock_); + bundleStatusMap_[bundleName].indexFile = cache.GetExtManage(); + TryClearBundleOfMap(bundleName); + } + + void TryNotify(bool flag = false) + { + if (flag == true) { + ready_ = true; + cv_.notify_all(); + } else if (bundleStatusMap_.size() == 0 && cnt_ == 0 && isAllBundelsFinished.load()) { + ready_ = true; + cv_.notify_all(); + } + } + + void UpdateBundleFinishedCount() + { + lock_guard lk(lock_); + cnt_--; + } + + void SetBundleFinishedCount(uint32_t cnt) + { + cnt_ = cnt; + } + + void Wait() + { + unique_lock lk(lock_); + cv_.wait(lk, [&] { return ready_; }); + } + + unique_ptr session_ = {}; + +private: + struct BundleStatus { + set receivedFile; + set indexFile; + }; + + void TryClearBundleOfMap(const BundleName &bundleName) + { + if (bundleStatusMap_[bundleName].indexFile == bundleStatusMap_[bundleName].receivedFile) { + bundleStatusMap_.erase(bundleName); + } + } + + map bundleStatusMap_; + mutable condition_variable cv_; + mutex lock_; + bool ready_ = false; + uint32_t cnt_ {0}; + +public: + std::atomic isAllBundelsFinished {false}; + vector lastIncrementalData {}; +}; + +static string GenHelpMsg() +{ + return "\t\tThis operation helps to backup application data.\n" + "\t\t--isLocal\t\t This parameter should be true or flase; true: local backup false: others.\n" + "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n" + "\t\t--bundle\t\t This parameter is bundleName."; +} + +static void OnFileReady(shared_ptr ctx, const BFileInfo &fileInfo, UniqueFd fd, UniqueFd manifestFd) +{ + printf("FileReady owner = %s, fileName = %s, fd = %d, manifestFd = %d\n", fileInfo.owner.c_str(), + fileInfo.fileName.c_str(), fd.Get(), manifestFd.Get()); + string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner; + if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + auto iter = find_if(ctx->lastIncrementalData.begin(), ctx->lastIncrementalData.end(), + [bundleName {fileInfo.owner}](const auto &data) { return bundleName == data.bundleName; }); + if (iter == ctx->lastIncrementalData.end()) { + throw BError(BError::Codes::TOOL_INVAL_ARG); + } + tmpPath = tmpPath + "/" + to_string(iter->lastIncrementalTime); + if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + // 数据文件保存 路径拼接方式: /data/backup/incrementalreceived/bundleName/时间戳/incremental/文件名 + string tmpDataPath = tmpPath + string(BConstants::BACKUP_TOOL_INCREMENTAL); + if (access(tmpDataPath.data(), F_OK) != 0 && mkdir(tmpDataPath.data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + UniqueFd fdLocal(open((tmpDataPath + "/" + fileInfo.fileName).data(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU)); + if (fdLocal < 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + BFile::SendFile(fdLocal, fd); + + // 简报文件保存 路径拼接方式: /data/backup/incrementalreceived/bundleName/时间戳/manifest/文件名.rp + string tmpmanifestPath = tmpPath + string(BConstants::BACKUP_TOOL_MANIFEST); + if (access(tmpmanifestPath.data(), F_OK) != 0 && mkdir(tmpmanifestPath.data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + UniqueFd fdManifest( + open((tmpmanifestPath + "/" + fileInfo.fileName + ".rp").data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)); + if (fdManifest < 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + BFile::SendFile(fdManifest, manifestFd); + if (fileInfo.fileName == BConstants::EXT_BACKUP_MANAGE) { + UniqueFd fullDatFd(open((string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner + + string(BConstants::BACKUP_TOOL_MANIFEST).append(".rp")) + .data(), + O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)); + BFile::SendFile(fullDatFd, fdManifest); + ctx->SetIndexFiles(fileInfo.owner, move(fd)); + } else { + ctx->UpdateBundleReceivedFiles(fileInfo.owner, fileInfo.fileName); + } + ctx->TryNotify(); +} + +static void OnBundleStarted(shared_ptr ctx, ErrCode err, const BundleName name) +{ + printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str()); + if (err != 0) { + ctx->isAllBundelsFinished.store(true); + ctx->UpdateBundleFinishedCount(); + ctx->TryNotify(); + } +} + +static void OnBundleFinished(shared_ptr ctx, ErrCode err, const BundleName name) +{ + printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str()); + ctx->UpdateBundleFinishedCount(); + ctx->TryNotify(); +} + +static void OnAllBundlesFinished(shared_ptr ctx, ErrCode err) +{ + ctx->isAllBundelsFinished.store(true); + if (err == 0) { + printf("all bundles backup finished end\n"); + } else { + printf("Failed to Unplanned Abort error: %d\n", err); + ctx->TryNotify(true); + return; + } + ctx->TryNotify(); +} + +static void OnBackupServiceDied(shared_ptr ctx) +{ + printf("backupServiceDied\n"); + ctx->TryNotify(true); +} + +static void BackupToolDirSoftlinkToBackupDir() +{ + // 判断BConstants::BACKUP_TOOL_LINK_DIR 是否是软链接 + if (access(BConstants::BACKUP_TOOL_LINK_DIR.data(), F_OK) == 0) { + struct stat inStat = {}; + if (lstat(BConstants::BACKUP_TOOL_LINK_DIR.data(), &inStat) == -1) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + + if ((inStat.st_mode & S_IFMT) == S_IFLNK) { + return; + } + // 非软连接删除重新创建 + if (!ForceRemoveDirectory(BConstants::BACKUP_TOOL_LINK_DIR.data())) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + } + + if (access(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), F_OK) != 0 && + mkdir(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + if (symlink(BConstants::GetSaBundleBackupToolDir(BConstants::DEFAULT_USER_ID).data(), + BConstants::BACKUP_TOOL_LINK_DIR.data()) == -1) { + HILOGE("failed to create soft link file %{public}s errno : %{public}d", + BConstants::BACKUP_TOOL_LINK_DIR.data(), errno); + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + + if (access((BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR).data(), F_OK) != 0 && + mkdir((BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR).data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } +} + +static int GetLocalCapabilitiesIncremental(shared_ptr ctx, + const string &pathCapFile, + const vector &bundleNames, + const vector ×) +{ + if (bundleNames.size() != times.size()) { + fprintf(stderr, "Inconsistent amounts of bundles and incrementalTime!\n"); + return -EPERM; + } + UniqueFd fdLocal(open(pathCapFile.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)); + if (fdLocal < 0) { + fprintf(stderr, "Failed to open file. error: %d %s\n", errno, strerror(errno)); + return -EPERM; + } + auto proxy = ServiceProxy::GetInstance(); + if (!proxy) { + fprintf(stderr, "Get an empty backup sa proxy\n"); + return -EPERM; + } + int num = 0; + for (auto &bundleName : bundleNames) { + BIncrementalData data; + data.bundleName = bundleName; + data.lastIncrementalTime = atoi(times[num].c_str()); + ctx->lastIncrementalData.push_back(data); + num++; + } + UniqueFd fd = proxy->GetLocalCapabilitiesIncremental(ctx->lastIncrementalData); + if (fd < 0) { + fprintf(stderr, "error GetLocalCapabilitiesIncremental"); + } else { + BFile::SendFile(fdLocal, fd); + } + return 0; +} + +static int32_t Init(const string &pathCapFile, vector bundleNames, vector times) +{ + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init"); + // SELinux backup_tool工具/data/文件夹下创建文件夹 SA服务因root用户的自定义标签无写入权限 此处调整为软链接形式 + BackupToolDirSoftlinkToBackupDir(); + + auto ctx = make_shared(); + ctx->session_ = BIncrementalBackupSession::Init(BIncrementalBackupSession::Callbacks { + .onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2, placeholders::_3), + .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2), + .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2), + .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1), + .onBackupServiceDied = bind(OnBackupServiceDied, ctx)}); + if (ctx->session_ == nullptr) { + printf("Failed to init backup\n"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return -EPERM; + } + + if (int ret = GetLocalCapabilitiesIncremental(ctx, pathCapFile, bundleNames, times); ret != 0) { + return ret; + } + vector bundlesToBackup; + for (auto &data : ctx->lastIncrementalData) { + string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + data.bundleName; + if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) { + throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno)); + } + tmpPath = tmpPath + string(BConstants::BACKUP_TOOL_MANIFEST).append(".rp"); + data.manifestFd = open(tmpPath.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + } + int ret = ctx->session_->AppendBundles(ctx->lastIncrementalData); + if (ret != 0) { + printf("backup append bundles error: %d\n", ret); + throw BError(BError::Codes::TOOL_INVAL_ARG, "backup append bundles error"); + } + + ctx->SetBundleFinishedCount(bundleNames.size()); + ctx->Wait(); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + ctx->session_->Release(); + return 0; +} + +static int g_exec (map> &mapArgToVal) +{ + if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() || + mapArgToVal.find("incrementalTime") == mapArgToVal.end()) { + return -EPERM; + } + return Init(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"], mapArgToVal["incrementalTime"]); +} + +bool IncrementalBackUpRegister() +{ + return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor { + .opName = {"incrementalbackup"}, + .argList = {{ + .paramName = "pathCapFile", + .repeatable = false, + }, + { + .paramName = "bundles", + .repeatable = true, + }, + { + .paramName = "incrementalTime", + .repeatable = true, + }}, + .funcGenHelpMsg = GenHelpMsg, + .funcExec = g_exec , + }}); +} +} // namespace OHOS::FileManagement::Backup \ No newline at end of file -- Gitee