diff --git a/frameworks/native/backup_ext/include/ext_extension.h b/frameworks/native/backup_ext/include/ext_extension.h index c39e05fcd9dbdfcc9d29cc48be36a806f5c75ec2..77151d99f99cc747b1eb5cbc01b1d1814d591f25 100644 --- a/frameworks/native/backup_ext/include/ext_extension.h +++ b/frameworks/native/backup_ext/include/ext_extension.h @@ -21,6 +21,7 @@ #include #include "b_json/b_json_entity_extension_config.h" +#include "b_json/b_json_entity_ext_manage.h" #include "b_resources/b_constants.h" #include "ext_backup_js.h" #include "ext_extension_stub.h" @@ -95,9 +96,15 @@ private: void AsyncTaskOnBackup(); + bool isIndexReadyForRestore(std::vector indexInfos_); + + bool getInfoFromIndexFile(std::vector indexInfos_); + private: std::shared_mutex lock_; std::shared_ptr extension_; + std::vector indexInfos_; + std::vector waitedFiles_; std::vector tars_; OHOS::ThreadPool threadPool_; }; diff --git a/frameworks/native/backup_ext/src/ext_extension.cpp b/frameworks/native/backup_ext/src/ext_extension.cpp index 987333bb3086a03a12a291bfe6004b1c08dbf85c..f8cc976c0cb10dcf446eca9cfd445fd543145477 100644 --- a/frameworks/native/backup_ext/src/ext_extension.cpp +++ b/frameworks/native/backup_ext/src/ext_extension.cpp @@ -42,8 +42,6 @@ #include "b_tarball/b_tarball_factory.h" #include "filemgmt_libhilog.h" #include "service_proxy.h" -#include "tar_file.h" -#include "untar_file.h" namespace OHOS::FileManagement::Backup { const string DEFAULT_TAR_PKG = "1.tar"; @@ -55,6 +53,8 @@ const string INDEX_FILE_RESTORE = string(BConstants::PATH_BUNDLE_BACKUP_HOME). append(BConstants::EXT_BACKUP_MANAGE); using namespace std; +static set indexFileInfos; + void BackupExtExtension::VerifyCaller() { HILOGI("begin"); @@ -70,6 +70,86 @@ void BackupExtExtension::VerifyCaller() } } +static void createDirectory(const string &path) +{ + string::size_type index = 0; + do { + string subPath; + index = path.find('/', index + 1); + if (index == string::npos) { + subPath = path; + } else { + subPath = path.substr(0, index); + } + if (access(subPath.c_str(), F_OK) != 0) { + if (mkdir(subPath.c_str(), (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0 && errno != EEXIST) { + string str = string("Failed to create folder. ").append(std::generic_category().message(errno)); + throw BError(BError::Codes::EXT_INVAL_ARG, str); + } + } + } while (index != string::npos); +} + +bool BackupExtExtension::isIndexReadyForRestore(std::vector indexInfos_) +{ + shared_lock lock(lock_); + if (indexInfos_.empty()) { + return false; + } + return true; +} + +bool BackupExtExtension::getInfoFromIndexFile(std::vector indexInfos_) +{ + if (access(INDEX_FILE_RESTORE.data(), F_OK) != 0) { + HILOGE("Index file does not exist"); + return false; + } + + // 获取索引文件内容 + BJsonCachedEntity cachedEntity(UniqueFd(open(INDEX_FILE_RESTORE.data(), O_RDONLY))); + auto cache = cachedEntity.Structuralize(); + // 写入索引信息加锁 + unique_lock lock(lock_); + indexInfos_ = cache.GetExtManageInfo(); + return true; +} + +static UniqueFd createFileFromIndexFile(ExtManageInfo& manageInfo) +{ + string filePath = manageInfo.fileName; + struct stat sta = manageInfo.sta; + + // 创建目录 + createDirectory(filePath.data()); + + // 创建FD + auto fd = UniqueFd(open(filePath.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)); + struct timespec tv[2] = {sta.st_atim, sta.st_mtim}; + if (futimens(fd.Get(), tv) != 0) { + HILOGE("failed to change the file time. %{public}s , %{public}d", filePath.c_str(), errno); + } + return fd; +} + +static bool IsUserTar(const string &tarFile, const string &indexFile) +{ + if (tarFile.empty()) { + return false; + } + BJsonCachedEntity cachedEntity(UniqueFd(open(indexFile.data(), O_RDONLY))); + auto cache = cachedEntity.Structuralize(); + auto info = cache.GetExtManageInfo(); + for (auto &item : info) { + if (item.hashName == tarFile) { + HILOGI("tarFile:%{public}s isUserTar:%{public}d", tarFile.data(), item.isUserTar); + return item.isUserTar; + } + } + HILOGE("Can not find tarFile %{public}s", tarFile.data()); + return false; +} + UniqueFd BackupExtExtension::GetFileHandle(const string &fileName) { try { @@ -78,10 +158,41 @@ UniqueFd BackupExtExtension::GetFileHandle(const string &fileName) throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid"); } + // 如果是manage.json + if (strcmp(fileName.data(), BConstants::EXT_BACKUP_MANAGE.data()) == 0) { + HILOGI("Index file is published before receive getFileHandle. "); + return UniqueFd(-1); + } + VerifyCaller(); - if (fileName.find('/') != string::npos) { - throw BError(BError::Codes::EXT_INVAL_ARG, "Filename is not valid"); + // 查看manage.json信息是否已经获取 + if (!isIndexReadyForRestore(indexInfos_)) { + HILOGI("Index file is not ready for restore"); + waitedFiles_.emplace_back(fileName); + return UniqueFd(-1); + } + + // 从Manage.json中获取对应的文件信息 + ExtManageInfo manageInfo; + bool isFind = false; + for (auto &item : indexInfos_) { + if (strcmp(item.hashName.data(), fileName.data()) == 0) { + manageInfo = item; + isFind = true; + break; + } + } + + if (!isFind) { + throw BError(BError::Codes::EXT_INVAL_ARG, "failed to get the fileName from manage.json"); + return UniqueFd(-1); + } + + // 普通大文件或者userTar + if (ExtractFileExt(fileName) != "tar" || IsUserTar(fileName, INDEX_FILE_RESTORE)) { + HILOGI("FileName contains path symbol."); + return createFileFromIndexFile(manageInfo); } string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); @@ -113,7 +224,7 @@ ErrCode BackupExtExtension::HandleClear() return ERR_OK; } -static ErrCode IndexFileReady(const TarMap &pkgInfo, sptr proxy) +static ErrCode IndexFileReady(const map> &pkgInfo, sptr proxy) { BJsonCachedEntity cachedEntity( UniqueFd(open(INDEX_FILE_BACKUP.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR))); @@ -197,6 +308,30 @@ ErrCode BackupExtExtension::PublishFile(const string &fileName) } VerifyCaller(); + // 对于manage.json,需要读取文件信息到容器 + if (strcmp(fileName.data(), BConstants::EXT_BACKUP_MANAGE.data()) == 0) { + if (!getInfoFromIndexFile(indexInfos_)) { + throw BError(BError::Codes::EXT_INVAL_ARG, "Index file not exist"); + } + for (auto &waitedFile : waitedFiles_) { + auto task = [obj {wptr(this)}, waitedFile]() { + auto ptr = obj.promote(); + ptr->GetFileHandle(waitedFile); + }; + // 读取等待中的waitedFiles_,触发操作 + threadPool_.AddTask([task]() { + try { + task(); + } catch (...) { + HILOGE("Failed to add task to thread pool"); + } + }); + } + unique_lock lock(lock_); + tars_.push_back(fileName); + return ERR_OK; + } + string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); string tarName = path + fileName; { @@ -244,32 +379,15 @@ ErrCode BackupExtExtension::HandleBackup() return 0; } -static bool IsUserTar(const string &tarFile, const string &indexFile) +static map> GetBigFileInfo(const vector &includes, + const vector &excludes) { - if (tarFile.empty()) { - return false; - } - BJsonCachedEntity cachedEntity(UniqueFd(open(indexFile.data(), O_RDONLY))); - auto cache = cachedEntity.Structuralize(); - auto info = cache.GetExtManageInfo(); - for (auto &item : info) { - if (item.hashName == tarFile) { - HILOGI("tarFile:%{public}s isUserTar:%{public}d", tarFile.data(), item.isUserTar); - return item.isUserTar; - } - } - HILOGE("Can not find tarFile %{public}s", tarFile.data()); - return false; -} - -static pair> GetFileInfos(const vector &includes, const vector &excludes) -{ - auto [errCode, files, smallFiles] = BDir::GetBigFiles(includes, excludes); + auto [errCode, files] = BDir::GetBigFiles(includes, excludes); if (errCode != 0) { return {}; } - auto GetStringHash = [](const TarMap &m, const string &str) -> string { + auto GetStringHash = [](const map> &m, const string &str) -> string { ostringstream strHex; strHex << hex; @@ -286,7 +404,7 @@ static pair> GetFileInfos(const vector &includes, return name; }; - TarMap bigFiles; + map> bigFiles; for (const auto &item : files) { string md5Name = GetStringHash(bigFiles, item.first); if (!md5Name.empty()) { @@ -294,7 +412,7 @@ static pair> GetFileInfos(const vector &includes, } } - return {bigFiles, smallFiles}; + return bigFiles; } int BackupExtExtension::DoBackup(const BJsonEntityExtensionConfig &usrConfig) @@ -313,7 +431,7 @@ int BackupExtExtension::DoBackup(const BJsonEntityExtensionConfig &usrConfig) vector excludes = usrConfig.GetExcludes(); // 大文件处理 - auto [bigFileInfo, smallFiles] = GetFileInfos(includes, excludes); + auto bigFileInfo = GetBigFileInfo(includes, excludes); for (const auto &item : bigFileInfo) { auto filePath = std::get<0>(item.second); if (!filePath.empty()) { @@ -321,10 +439,18 @@ int BackupExtExtension::DoBackup(const BJsonEntityExtensionConfig &usrConfig) } } - // 分片打包 - TarMap tarMap {}; - TarFile::GetInstance().Packet(smallFiles, "part", path, tarMap); - bigFileInfo.insert(tarMap.begin(), tarMap.end()); + string tarName = path + DEFAULT_TAR_PKG; + string root = "/"; + + // 打包 + auto tarballTar = BTarballFactory::Create("cmdline", tarName); + (tarballTar->tar)(root, {includes.begin(), includes.end()}, {excludes.begin(), excludes.end()}); + + struct stat sta = {}; + if (stat(tarName.data(), &sta) == -1) { + HILOGE("failed to invoke the system function stat, %{public}s", tarName.c_str()); + } + bigFileInfo.emplace(DEFAULT_TAR_PKG, make_tuple(tarName, sta, false)); auto proxy = ServiceProxy::GetInstance(); if (proxy == nullptr) { @@ -349,7 +475,14 @@ int BackupExtExtension::DoRestore(const string &fileName) // REM: 解压启动Extension时即挂载好的备份目录中的数据 string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); string tarName = path + fileName; - UntarFile::GetInstance().UnPacket(tarName, "/"); + HILOGI("tarName:%{public}s, fileName:%{public}s", tarName.c_str(), fileName.c_str()); + + auto tarballFunc = BTarballFactory::Create("cmdline", tarName); + if (extension_->WasFromSpeicalVersion() || extension_->UseFullBackupOnly()) { + (tarballFunc->untar)(path); + } else { + (tarballFunc->untar)("/"); + } HILOGI("Application recovered successfully, package path is %{public}s", tarName.c_str()); return ERR_OK; @@ -418,17 +551,13 @@ static void RestoreBigFiles() HILOGE("file path is empty. %{public}s", filePath.c_str()); continue; } - if (!BFile::CopyFile(fileName, filePath)) { - HILOGE("failed to copy the file. err = %{public}d", errno); - continue; - } - if (chmod(filePath.c_str(), item.sta.st_mode) != 0) { - HILOGE("Failed to chmod %{public}s, err = %{public}d", filePath.c_str(), errno); - } - if (fileName != filePath) { - if (!RemoveFile(fileName)) { - HILOGE("Failed to delete the big file %{public}s", fileName.c_str()); + if (rename(fileName.data(), filePath.data()) != 0) { + HILOGE("failed to rename the file, try to copy it. err = %{public}d", errno); + if (!BFile::CopyFile(fileName, filePath)) { + HILOGE("failed to copy the file. err = %{public}d", errno); + continue; } + HILOGI("succeed to rename or copy the file"); } set lks = cache.GetHardLinkInfo(item.hashName); for (const auto &lksPath : lks) { @@ -448,21 +577,15 @@ static void RestoreBigFiles() static void DeleteBackupTars() { // 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 path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); + string tarPath = path + DEFAULT_TAR_PKG; + string indexPath = path + string(BConstants::EXT_BACKUP_MANAGE); + if (!RemoveFile(tarPath)) { + HILOGE("Failed to delete the backup tar %{public}s", tarPath.c_str()); } - if (!RemoveFile(INDEX_FILE_RESTORE)) { - HILOGE("Failed to delete the backup index %{public}s", INDEX_FILE_RESTORE.c_str()); + + if (!RemoveFile(indexPath)) { + HILOGE("Failed to delete the backup index %{public}s", indexPath.c_str()); } } @@ -650,6 +773,17 @@ ErrCode BackupExtExtension::HandleRestore() if (extension_->WasFromSpeicalVersion() && extension_->RestoreDataReady()) { HILOGI("Restore directly when upgrading."); AsyncTaskRestoreForUpgrade(); + } else { + // To provide fd for manage.json first. + auto proxy = ServiceProxy::GetInstance(); + ErrCode ret = + proxy->AppFileReady(string(BConstants::EXT_BACKUP_MANAGE), UniqueFd(open(INDEX_FILE_BACKUP.data(), O_RDONLY))); + if (SUCCEEDED(ret)) { + HILOGI("Restore start: Manage.json request successfully"); + } else { + HILOGI("Restore start: AppFileReady interface fails to be invoked: %{public}d", ret); + } + return ret; } return 0; diff --git a/services/backup_sa/src/module_ipc/service.cpp b/services/backup_sa/src/module_ipc/service.cpp index 937146137211e0a1781b5604f962f71c147f19d0..93d5c85dcd5a68f5edade60424099b291d065863 100644 --- a/services/backup_sa/src/module_ipc/service.cpp +++ b/services/backup_sa/src/module_ipc/service.cpp @@ -353,9 +353,6 @@ ErrCode Service::PublishFile(const BFileInfo &fileInfo) sched_->Sched(fileInfo.owner); return BError(BError::Codes::OK); } - if (fileInfo.fileName.find('/') != string::npos) { - throw BError(BError::Codes::SA_INVAL_ARG, "Filename is not valid"); - } auto backUpConnection = session_->GetExtConnection(fileInfo.owner); @@ -513,9 +510,6 @@ ErrCode Service::GetFileHandle(const string &bundleName, const string &fileName) } return BError(BError::Codes::OK); } - if (fileName.find('/') != string::npos) { - throw BError(BError::Codes::SA_INVAL_ARG, "Filename is not valid"); - } auto action = session_->GetServiceSchedAction(bundleName); if (action == BConstants::ServiceSchedAction::RUNNING) { auto backUpConnection = session_->GetExtConnection(bundleName);