diff --git a/frameworks/native/backup_ext/include/ext_extension.h b/frameworks/native/backup_ext/include/ext_extension.h index 5120b9285761a7f54632f3790d7fc9ba5344572e..236c038cec829923628317832be9ea5efc57b073 100644 --- a/frameworks/native/backup_ext/include/ext_extension.h +++ b/frameworks/native/backup_ext/include/ext_extension.h @@ -23,6 +23,7 @@ #include +#include "b_json/b_json_entity_ext_manage.h" #include "b_json/b_json_entity_extension_config.h" #include "b_json/b_report_entity.h" #include "b_resources/b_constants.h" @@ -45,6 +46,7 @@ public: std::tuple GetIncrementalBackupFileHandle() override; void AsyncTaskRestoreForUpgrade(void); + void AsyncTaskRestoreForManageJsonFirst(void); void ExtClear(void); void AsyncTaskIncrementalRestoreForUpgrade(void); @@ -139,6 +141,16 @@ private: std::shared_mutex lock_; std::shared_ptr extension_; std::vector tars_; + /** + * @brief map include manageJson items if manageJson is first received file in PublishFile + * + */ + std::map manageJsonItems_; + /** + * @brief manageJson is first received file in PublishFile but other file is received in GetFileHandle before + * + */ + bool noRealManageJsonFirst_; OHOS::ThreadPool threadPool_; }; } // 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 8f81a35c1f71c9f0043929bffabb7f939bf14066..22ed3c9172942e8544b939d1ed42cecf0adfb460 100644 --- a/frameworks/native/backup_ext/src/ext_extension.cpp +++ b/frameworks/native/backup_ext/src/ext_extension.cpp @@ -105,7 +105,7 @@ static bool CheckAndCreateDirectory(const string &filePath) static UniqueFd GetFileHandleForSpecialCloneCloud(const string &fileName) { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); - HILOGE("GetFileHandleForSpecialCloneCloud: fileName is %{public}s", fileName.data()); + HILOGI("GetFileHandleForSpecialCloneCloud: fileName is %{public}s", fileName.data()); string filePath = fileName; if (fileName.front() != BConstants::FILE_SEPARATOR_CHAR) { filePath = BConstants::FILE_SEPARATOR_CHAR + fileName; @@ -126,6 +126,54 @@ static UniqueFd GetFileHandleForSpecialCloneCloud(const string &fileName) return UniqueFd(open(fileName.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)); } +static UniqueFd GetFileHandleForManageJsonFirst(const string &fileName, map manageJsonItems) +{ + HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); + HILOGI("GetFileHandleForManageJsonFirst: fileName is %{public}s", fileName.data()); + + auto it = manageJsonItems.find(fileName); + if (it == manageJsonItems.end()) { + HILOGE("GetFileHandleForManageJsonFirst: FileName not match the manage.json"); + throw BError(BError::Codes::EXT_INVAL_ARG, fileName); + } + auto item = it->second; + string filePath = item.fileName; + if (!item.isUserTar && !item.isBigFile) { + // tar files (not user tar) will be moved to temporary directory as before + 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(generic_category().message(errno)); + throw BError(BError::Codes::EXT_INVAL_ARG, str); + } + + filePath = path + fileName; + if (access(filePath.data(), F_OK) == 0) { + throw BError(BError::Codes::EXT_INVAL_ARG, string("The file already exists")); + } + } else { + // big file and user tar would be moved to destination path defined in manage.json + size_t filePathPrefix = filePath.find_last_of(BConstants::FILE_SEPARATOR_CHAR); + if (filePathPrefix == string::npos) { + HILOGE("GetFileHandleForManageJsonFirst: Invalid fileName"); + throw BError(BError::Codes::EXT_INVAL_ARG, fileName); + } + string path = filePath.substr(0, filePathPrefix); + if (access(path.data(), F_OK) != 0) { + bool created = ForceCreateDirectory(path.data()); + if (!created) { + string str = string("Failed to create restore folder."); + throw BError(BError::Codes::EXT_INVAL_ARG, str); + } + } + } + UniqueFd fd(open(filePath.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)); + if (fd < 0) { + HILOGE("Open file failed, file name is %{private}s, err = %{public}d", filePath.data(), errno); + return UniqueFd(-1); + } + return fd; +} + UniqueFd BackupExtExtension::GetFileHandle(const string &fileName) { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); @@ -141,6 +189,15 @@ UniqueFd BackupExtExtension::GetFileHandle(const string &fileName) return GetFileHandleForSpecialCloneCloud(fileName); } + // manageJsonItems_ is not means manageJson is first received in publishFile + // but need to check if other file is received in GetFileHandle before manageJson received in publishFile + if (!manageJsonItems_.empty() && !noRealManageJsonFirst_ && fileName != BConstants::EXT_BACKUP_MANAGE) { + return GetFileHandleForManageJsonFirst(fileName, manageJsonItems_); + } else if (fileName != BConstants::EXT_BACKUP_MANAGE) { + // other file receive here before manageJson in publishFile, this symbol will be true in session + noRealManageJsonFirst_ = true; + } + 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)); @@ -319,10 +376,53 @@ static bool IsAllFileReceived(vector tars, bool isSpeicalVersion) return includes(tars.begin(), tars.end(), info.begin(), info.end()); } +static void ReadManageJsonInfo(std::map &manageJsonItems) +{ + HILOGE("ReadManageJsonInfo: readManageJson"); + UniqueFd fd(open(INDEX_FILE_RESTORE.data(), O_RDONLY)); + if (fd < 0) { + HILOGE("ReadManageJsonInfo: open manage json file failed, err = %{public}d", errno); + throw BError(BError::Codes::EXT_INVAL_ARG, "Open manage json file failed"); + } + + BJsonCachedEntity cachedEntity(move(fd)); + auto cache = cachedEntity.Structuralize(); + auto info = cache.GetExtManageInfo(); + for (auto &item : info) { + if (item.hashName.empty()) { + continue; + } + manageJsonItems.emplace(item.hashName, item); + } +} + +static bool CheckAndGetFilePathForManageJsonFirst(std::map &manageJsonItems, + const string &fileName, string &tarName, bool &noRealManageJsonFirst) +{ + // manageJsonItems_ is not means manageJson is first received in publishFile, + // but need to check noRealManageJsonFirst_ if other file is received in GetFileHandle + // before manageJson received in publishFile + if (!manageJsonItems.empty() && fileName != BConstants::EXT_BACKUP_MANAGE && !noRealManageJsonFirst) { + HILOGE("!!!!!PublishFile: manageJsonItems_ is not empty and file is not manage.json"); + auto it = manageJsonItems.find(fileName); + if (it == manageJsonItems.end()) { + HILOGE("PublishFile: FileName not match the manage.json"); + throw BError(BError::Codes::EXT_INVAL_ARG, fileName); + } + // destination file is real path not temporary path for big file and user tar + auto manageItem = it->second; + if (manageItem.isUserTar || manageItem.isBigFile) { + tarName = manageItem.fileName; + } + return true; + } + return false; +} + ErrCode BackupExtExtension::PublishFile(const string &fileName) { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); - HILOGE("begin publish file. fileName is %{public}s", fileName.data()); + HILOGI("begin publish file. fileName is %{public}s", fileName.data()); try { if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) { throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid"); @@ -331,10 +431,21 @@ ErrCode BackupExtExtension::PublishFile(const string &fileName) // 是否指定克隆模式 bool isSpeicalVersion = extension_->SpeicalVersionForCloneAndCloud(); + // check if manage.json is the first received file in publishFile + // if Yes, read it and save its item infos to manageJsonItems_ map. + if (fileName == BConstants::EXT_BACKUP_MANAGE && tars_.empty()) { + HILOGI("!!!!!!PublishFile: manage.json is first received"); + ReadManageJsonInfo(manageJsonItems_); + } + string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE); string tarName = isSpeicalVersion ? fileName : path + fileName; + // Check if manageJson is the first received file in restore session + bool isManageJsonFirst = CheckAndGetFilePathForManageJsonFirst( + manageJsonItems_, fileName, tarName, noRealManageJsonFirst_); + HILOGE("!!!!!PublishFile: tarName = %{public}s", tarName.c_str()); { - BExcepUltils::VerifyPath(tarName, !isSpeicalVersion); + BExcepUltils::VerifyPath(tarName, !isSpeicalVersion && !isManageJsonFirst); 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"); @@ -346,9 +457,12 @@ ErrCode BackupExtExtension::PublishFile(const string &fileName) } // 异步执行解压操作 if (extension_->AllowToBackupRestore()) { - AsyncTaskRestore(); + if (isManageJsonFirst) { + AsyncTaskRestoreForManageJsonFirst(); + } else { + AsyncTaskRestore(); + } } - return ERR_OK; } catch (const BError &e) { DoClear(); @@ -528,6 +642,7 @@ int BackupExtExtension::DoRestore(const string &fileName) 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); @@ -615,7 +730,7 @@ void BackupExtExtension::AsyncTaskBackup(const string config) }); } -static void RestoreBigFilesForSpecialCloneCloud(ExtManageInfo item) +static void RestoreBigFilesForDirectCopy(ExtManageInfo item) { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); struct stat &sta = item.sta; @@ -631,6 +746,18 @@ static void RestoreBigFilesForSpecialCloneCloud(ExtManageInfo item) } } +static ErrCode RestoreTarForManageJsonFirst(ExtManageInfo item) +{ + HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); + string tarName = item.fileName; + UntarFile::GetInstance().UnPacket(tarName, "/"); + + if (!RemoveFile(tarName)) { + HILOGE("Failed to delete the backup tar %{public}s", tarName.c_str()); + } + return ERR_OK; +} + static ErrCode RestoreTarForSpeicalCloneCloud(ExtManageInfo item) { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); @@ -651,6 +778,33 @@ static ErrCode RestoreTarForSpeicalCloneCloud(ExtManageInfo item) return ERR_OK; } +static ErrCode RestoreFilesForManageJsonFirst(const map manageJsonItems) +{ + HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); + for (auto it = manageJsonItems.begin(); it != manageJsonItems.end(); ++it) { + HILOGE("!!!!!RestoreFilesForManageJsonFirst: item keys = %{public}s", it->first.c_str()); + auto item = it->second; + if (item.hashName.empty()) { + continue; + } + if (item.isUserTar || item.isBigFile) { + // 大文件处理 + RestoreBigFilesForDirectCopy(item); + } else { + // 待解压tar文件处理 + if (RestoreTarForManageJsonFirst(item) != ERR_OK) { + HILOGE("Failed to restore tar file %{public}s", item.hashName.c_str()); + return ERR_INVALID_VALUE; + } + } + } + if (!RemoveFile(INDEX_FILE_RESTORE)) { + HILOGE("Failed to delete the backup index %{public}s", INDEX_FILE_RESTORE.c_str()); + } + HILOGI("after extra, do restore for ManageJsonFirst."); + return ERR_OK; +} + static ErrCode RestoreFilesForSpecialCloneCloud() { HITRACE_METER_NAME(HITRACE_TAG_FILEMANAGEMENT, __PRETTY_FUNCTION__); @@ -666,7 +820,7 @@ static ErrCode RestoreFilesForSpecialCloneCloud() } if (item.isUserTar || item.isBigFile) { // 大文件处理 - RestoreBigFilesForSpecialCloneCloud(item); + RestoreBigFilesForDirectCopy(item); } else { // 待解压tar文件处理 if (RestoreTarForSpeicalCloneCloud(item) != ERR_OK) { @@ -819,6 +973,43 @@ static void DeleteBackupIncrementalTars() } } +void BackupExtExtension::AsyncTaskRestoreForManageJsonFirst() +{ + auto task = [obj {wptr(this)}, tars {tars_}]() { + auto ptr = obj.promote(); + BExcepUltils::BAssert(ptr, BError::Codes::EXT_BROKEN_FRAMEWORK, "Ext extension handle have already released"); + try { + int ret = RestoreFilesForManageJsonFirst(ptr->manageJsonItems_); + if (ret == ERR_OK) { + HILOGI("after extra, do 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::AsyncTaskRestore() { auto task = [obj {wptr(this)}, tars {tars_}]() { @@ -828,13 +1019,6 @@ void BackupExtExtension::AsyncTaskRestore() int ret = ERR_OK; if (ptr->extension_->SpeicalVersionForCloneAndCloud()) { ret = RestoreFilesForSpecialCloneCloud(); - if (ret == ERR_OK) { - ptr->AsyncTaskRestoreForUpgrade(); - } else { - ptr->AppDone(ret); - ptr->DoClear(); - } - return; } // 解压 for (auto item : tars) { // 处理要解压的tar文件 @@ -844,8 +1028,8 @@ void BackupExtExtension::AsyncTaskRestore() } // 恢复用户tar包以及大文件 // 目的地址是否需要拼接path(临时目录),FullBackupOnly为true并且非特殊场景 - bool appendTargetPath = - ptr->extension_->UseFullBackupOnly() && !ptr->extension_->SpeicalVersionForCloneAndCloud(); + bool appendTargetPath = ptr->extension_->UseFullBackupOnly() && + !ptr->extension_->SpeicalVersionForCloneAndCloud(); RestoreBigFiles(appendTargetPath); // delete 1.tar/manage.json @@ -1050,6 +1234,8 @@ void BackupExtExtension::DoClear() string(BConstants::PATH_BUNDLE_BACKUP_HOME_EL1).append(BConstants::SA_BUNDLE_BACKUP_RESTORE)); unique_lock lock(lock_); tars_.clear(); + manageJsonItems_.clear(); + noRealManageJsonFirst_ = false; } catch (...) { HILOGI("Failed to clear"); }