• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ext_extension.h"
17 
18 #include <algorithm>
19 #include <iomanip>
20 #include <regex>
21 #include <string>
22 #include <vector>
23 
24 #include <sys/stat.h>
25 #include <unistd.h>
26 
27 #include <directory_ex.h>
28 #include <unique_fd.h>
29 
30 #include "accesstoken_kit.h"
31 #include "bundle_mgr_client.h"
32 #include "errors.h"
33 #include "ipc_skeleton.h"
34 
35 #include "b_error/b_error.h"
36 #include "b_error/b_excep_utils.h"
37 #include "b_filesystem/b_dir.h"
38 #include "b_filesystem/b_file.h"
39 #include "b_json/b_json_cached_entity.h"
40 #include "b_json/b_json_entity_ext_manage.h"
41 #include "b_resources/b_constants.h"
42 #include "b_tarball/b_tarball_factory.h"
43 #include "filemgmt_libhilog.h"
44 #include "service_proxy.h"
45 
46 namespace OHOS::FileManagement::Backup {
47 using namespace std;
48 
VerifyCaller()49 void BackupExtExtension::VerifyCaller()
50 {
51     HILOGI("begin");
52     uint32_t tokenCaller = IPCSkeleton::GetCallingTokenID();
53     int tokenType = Security::AccessToken::AccessTokenKit::GetTokenType(tokenCaller);
54     if (tokenType != Security::AccessToken::ATokenTypeEnum::TOKEN_NATIVE) {
55         throw BError(BError::Codes::EXT_BROKEN_IPC,
56                      string("Calling tokenType is error, token type is ").append(to_string(tokenType)));
57     }
58     if (IPCSkeleton::GetCallingUid() != BConstants::BACKUP_UID) {
59         throw BError(BError::Codes::EXT_BROKEN_IPC,
60                      string("Calling uid is invalid, calling uid is ").append(to_string(IPCSkeleton::GetCallingUid())));
61     }
62 }
63 
GetFileHandle(const string & fileName)64 UniqueFd BackupExtExtension::GetFileHandle(const string &fileName)
65 {
66     try {
67         if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) {
68             HILOGI("Failed to get file handle, because action is %{public}d invalid", extension_->GetExtensionAction());
69             throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid");
70         }
71 
72         VerifyCaller();
73 
74         if (!regex_match(fileName, regex("^[0-9a-zA-Z_.]+$"))) {
75             throw BError(BError::Codes::EXT_INVAL_ARG, "Filename is not alphanumeric");
76         }
77 
78         string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE);
79         if (mkdir(path.data(), S_IRWXU) && errno != EEXIST) {
80             string str = string("Failed to create restore folder. ").append(std::generic_category().message(errno));
81             throw BError(BError::Codes::EXT_INVAL_ARG, str);
82         }
83 
84         string tarName = path + fileName;
85         if (access(tarName.c_str(), F_OK) == 0) {
86             throw BError(BError::Codes::EXT_INVAL_ARG, string("The file already exists"));
87         }
88         return UniqueFd(open(tarName.data(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
89     } catch (...) {
90         HILOGE("Failed to get file handle");
91         DoClear();
92         return UniqueFd(-1);
93     }
94 }
95 
HandleClear()96 ErrCode BackupExtExtension::HandleClear()
97 {
98     HILOGI("begin clear");
99     if (extension_->GetExtensionAction() == BConstants::ExtensionAction::INVALID) {
100         throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid");
101     }
102     VerifyCaller();
103     DoClear();
104     return ERR_OK;
105 }
106 
IndexFileReady(const map<string,pair<string,struct stat>> & pkgInfo,sptr<IService> proxy)107 static ErrCode IndexFileReady(const map<string, pair<string, struct stat>> &pkgInfo, sptr<IService> proxy)
108 {
109     string indexFile = string(BConstants::PATH_BUNDLE_BACKUP_HOME).
110                             append(BConstants::SA_BUNDLE_BACKUP_BACKUP).
111                             append(BConstants::EXT_BACKUP_MANAGE);
112     BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(
113         UniqueFd(open(indexFile.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)));
114     auto cache = cachedEntity.Structuralize();
115     cache.SetExtManage(pkgInfo);
116     cachedEntity.Persist();
117     close(cachedEntity.GetFd().Release());
118 
119     ErrCode ret =
120         proxy->AppFileReady(string(BConstants::EXT_BACKUP_MANAGE), UniqueFd(open(indexFile.data(), O_RDONLY)));
121     if (SUCCEEDED(ret)) {
122         HILOGI("The application is packaged successfully");
123     } else {
124         HILOGI(
125             "The application is packaged successfully but the AppFileReady interface fails to be invoked: "
126             "%{public}d",
127             ret);
128     }
129     return ret;
130 }
131 
BigFileReady(sptr<IService> proxy)132 static ErrCode BigFileReady(sptr<IService> proxy)
133 {
134     string indexFile = string(BConstants::PATH_BUNDLE_BACKUP_HOME).
135                             append(BConstants::SA_BUNDLE_BACKUP_BACKUP).
136                             append(BConstants::EXT_BACKUP_MANAGE);
137     BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(UniqueFd(open(indexFile.data(), O_RDONLY)));
138     auto cache = cachedEntity.Structuralize();
139     map<string, pair<string, struct stat>> pkgInfo = cache.GetExtManageInfo();
140 
141     ErrCode ret {ERR_OK};
142     for (auto &item : pkgInfo) {
143         if (item.first.empty() || item.second.first.empty()) {
144             continue;
145         }
146 
147         UniqueFd fd(open(item.second.first.data(), O_RDONLY));
148         if (fd < 0) {
149             HILOGE("open file failed, file name is %{public}s", std::get<0>(item.second).c_str());
150             continue;
151         }
152 
153         ret = proxy->AppFileReady(item.first, std::move(fd));
154         if (SUCCEEDED(ret)) {
155             HILOGI("The application is packaged successfully, package name is %{public}s", item.first.c_str());
156         } else {
157             HILOGI(
158                 "The application is packaged successfully but the AppFileReady interface fails to be invoked: "
159                 "%{public}d",
160                 ret);
161         }
162     }
163     return ret;
164 }
165 
PublishFile(const string & fileName)166 ErrCode BackupExtExtension::PublishFile(const string &fileName)
167 {
168     HILOGE("begin publish file. fileName is %{public}s", fileName.data());
169     try {
170         if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) {
171             throw BError(BError::Codes::EXT_INVAL_ARG, "Action is invalid");
172         }
173         VerifyCaller();
174 
175         string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE);
176         string tarName = path + fileName;
177         {
178             BExcepUltils::VerifyPath(tarName, true);
179             unique_lock<shared_mutex> lock(lock_);
180             if (find(tars_.begin(), tars_.end(), fileName) != tars_.end() || access(tarName.data(), F_OK) != 0) {
181                 throw BError(BError::Codes::EXT_INVAL_ARG, "The file does not exist");
182             }
183             tars_.push_back(fileName);
184         }
185 
186         // 异步执行解压操作
187         if (extension_->AllowToBackupRestore()) {
188             AsyncTaskRestore();
189         }
190 
191         return ERR_OK;
192     } catch (const BError &e) {
193         DoClear();
194         return e.GetCode();
195     } catch (const exception &e) {
196         HILOGE("Catched an unexpected low-level exception %{public}s", e.what());
197         DoClear();
198         return BError(BError::Codes::EXT_BROKEN_FRAMEWORK).GetCode();
199     } catch (...) {
200         HILOGE("Unexpected exception");
201         DoClear();
202         return BError(BError::Codes::EXT_BROKEN_FRAMEWORK).GetCode();
203     }
204 }
205 
HandleBackup()206 ErrCode BackupExtExtension::HandleBackup()
207 {
208     string usrConfig = extension_->GetUsrConfig();
209     AsyncTaskBackup(usrConfig);
210     return 0;
211 }
212 
GetBigFileInfo(const vector<string> & includes,const vector<string> & excludes)213 static map<string, pair<string, struct stat>> GetBigFileInfo(const vector<string> &includes,
214                                                              const vector<string> &excludes)
215 {
216     auto [errCode, files] = BDir::GetBigFiles(includes, excludes);
217     if (errCode != 0) {
218         return {};
219     }
220 
221     auto GetStringHash = [](const map<string, pair<string, struct stat>> &m, const string &str) -> string {
222         ostringstream strHex;
223         strHex << hex;
224 
225         hash<string> strHash;
226         size_t szHash = strHash(str);
227         strHex << setfill('0') << setw(BConstants::BIG_FILE_NAME_SIZE) << szHash;
228         string name = strHex.str();
229         for (int i = 0; m.find(name) != m.end(); ++i, strHex.str("")) {
230             szHash = strHash(str + to_string(i));
231             strHex << setfill('0') << setw(BConstants::BIG_FILE_NAME_SIZE) << szHash;
232             name = strHex.str();
233         }
234 
235         return name;
236     };
237 
238     map<string, pair<string, struct stat>> bigFiles;
239     for (const auto &item : files) {
240         string md5Name = GetStringHash(bigFiles, item.first);
241         if (!md5Name.empty()) {
242             bigFiles.emplace(md5Name, make_pair(item.first, item.second));
243         }
244     }
245 
246     return bigFiles;
247 }
248 
DoBackup(const BJsonEntityExtensionConfig & usrConfig)249 int BackupExtExtension::DoBackup(const BJsonEntityExtensionConfig &usrConfig)
250 {
251     HILOGI("Do backup");
252     if (extension_->GetExtensionAction() != BConstants::ExtensionAction::BACKUP) {
253         return EPERM;
254     }
255 
256     string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_BACKUP);
257     if (mkdir(path.data(), S_IRWXU) && errno != EEXIST) {
258         throw BError(errno);
259     }
260 
261     vector<string> includes = usrConfig.GetIncludes();
262     vector<string> excludes = usrConfig.GetExcludes();
263     auto bigFileInfo = GetBigFileInfo(includes, excludes);
264     for (const auto &item : bigFileInfo) {
265         if (!item.second.first.empty()) {
266             excludes.push_back(item.second.first);
267         }
268     }
269 
270     string pkgName = "1.tar";
271     string tarName = path + pkgName;
272     string root = "/";
273 
274     // 打包
275     auto tarballTar = BTarballFactory::Create("cmdline", tarName);
276     (tarballTar->tar)(root, {includes.begin(), includes.end()}, {excludes.begin(), excludes.end()});
277 
278     struct stat sta = {};
279     if (stat(tarName.data(), &sta) == -1) {
280         HILOGE("failed to invoke the system function stat, %{public}s", tarName.c_str());
281     }
282     bigFileInfo.emplace(pkgName, make_pair(tarName, sta));
283 
284     auto proxy = ServiceProxy::GetInstance();
285     if (proxy == nullptr) {
286         throw BError(BError::Codes::EXT_BROKEN_BACKUP_SA, std::generic_category().message(errno));
287     }
288 
289     if (auto ret = IndexFileReady(bigFileInfo, proxy); ret) {
290         return ret;
291     }
292     auto res = BigFileReady(proxy);
293     HILOGE("HandleBackup finish, ret = %{public}d", res);
294     return res;
295 }
296 
DoRestore(const string & fileName)297 int BackupExtExtension::DoRestore(const string &fileName)
298 {
299     HILOGI("Do restore");
300     if (extension_->GetExtensionAction() != BConstants::ExtensionAction::RESTORE) {
301         return EPERM;
302     }
303     // REM: 给定version
304     // REM: 解压启动Extension时即挂载好的备份目录中的数据
305     string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE);
306     string tarName = path + fileName;
307 
308     auto tarballFunc = BTarballFactory::Create("cmdline", tarName);
309     if (extension_->WasFromSpeicalVersion()) {
310         (tarballFunc->untar)(path);
311     } else {
312         (tarballFunc->untar)("/");
313     }
314     HILOGI("Application recovered successfully, package path is %{public}s", tarName.c_str());
315 
316     return ERR_OK;
317 }
318 
AsyncTaskBackup(const string config)319 void BackupExtExtension::AsyncTaskBackup(const string config)
320 {
321     auto task = [obj {wptr<BackupExtExtension>(this)}, config]() {
322         auto ptr = obj.promote();
323         BExcepUltils::BAssert(ptr, BError::Codes::EXT_BROKEN_FRAMEWORK,
324                               "Ext extension handle have been already released");
325 
326         try {
327             BJsonCachedEntity<BJsonEntityExtensionConfig> cachedEntity(config);
328             auto cache = cachedEntity.Structuralize();
329             if (!cache.GetAllowToBackupRestore()) {
330                 HILOGI("Application does not allow backup or restore");
331                 return;
332             }
333             BExcepUltils::BAssert(ptr->extension_, BError::Codes::EXT_INVAL_ARG,
334                                   "extension handle have been already released");
335 
336             int ret = ptr->extension_->OnBackup();
337             if (ret == ERR_OK) {
338                 ret = ptr->DoBackup(cache);
339             }
340 
341             // REM: 处理返回结果 ret
342             ptr->AppDone(ret);
343             HILOGE("backup app done %{public}d", ret);
344         } catch (const BError &e) {
345             ptr->AppDone(e.GetCode());
346         } catch (const exception &e) {
347             HILOGE("Catched an unexpected low-level exception %{public}s", e.what());
348             ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode());
349         } catch (...) {
350             HILOGE("Failed to restore the ext bundle");
351             ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode());
352         }
353         // 清空备份目录
354         ptr->DoClear();
355     };
356 
357     // REM: 这里异步化了,需要做并发控制
358     // 在往线程池中投入任务之前将需要的数据拷贝副本到参数中,保证不发生读写竞争,
359     // 由于拷贝参数时尚运行在主线程中,故在参数拷贝过程中是线程安全的。
360     threadPool_.AddTask([task]() {
361         try {
362             task();
363         } catch (...) {
364             HILOGE("Failed to add task to thread pool");
365         }
366     });
367 }
368 
IsAllFileReceived(vector<string> tars)369 static bool IsAllFileReceived(vector<string> tars)
370 {
371     // 是否已收到索引文件
372     auto it = find(tars.begin(), tars.end(), string(BConstants::EXT_BACKUP_MANAGE));
373     if (tars.end() == it) {
374         return false;
375     }
376 
377     // 获取索引文件内容
378     string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE);
379     string indexFile = path + string(BConstants::EXT_BACKUP_MANAGE);
380     BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(UniqueFd(open(indexFile.data(), O_RDONLY)));
381     auto cache = cachedEntity.Structuralize();
382     set<string> info = cache.GetExtManage();
383 
384     // 从数量上判断是否已经全部收到
385     if (tars.size() <= info.size()) {
386         return false;
387     }
388 
389     // 逐个判断是否收到
390     sort(tars.begin(), tars.end());
391     if (includes(tars.begin(), tars.end(), info.begin(), info.end())) {
392         return true;
393     }
394     return false;
395 }
396 
RestoreBigFiles()397 static void RestoreBigFiles()
398 {
399     // 获取索引文件内容
400     string path = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE);
401     string indexFile = path + string(BConstants::EXT_BACKUP_MANAGE);
402     BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(UniqueFd(open(indexFile.data(), O_RDONLY)));
403     auto cache = cachedEntity.Structuralize();
404     auto info = cache.GetExtManageInfo();
405 
406     for (auto &item : info) {
407         if (item.first.empty() || item.first.size() < BConstants::BIG_FILE_NAME_SIZE ||
408             item.first.rfind('.') != string::npos) {
409             continue;
410         }
411 
412         string fileName = path + item.first;
413         auto [filePath, sta] = item.second;
414 
415         if (access(fileName.data(), F_OK) != 0) {
416             HILOGI("file dose not exist");
417             continue;
418         }
419         if (filePath.empty()) {
420             HILOGE("file path is empty. %{public}s", filePath.c_str());
421             continue;
422         }
423         if (rename(fileName.data(), filePath.data()) != 0) {
424             HILOGE("failed to rename the file, try to copy it. err = %{public}d", errno);
425             if (!BFile::CopyFile(fileName, filePath)) {
426                 HILOGE("failed to copy the file. err = %{public}d", errno);
427                 continue;
428             }
429             HILOGI("succeed to rename or copy the file");
430         }
431         set<string> lks = cache.GetHardLinkInfo(item.first);
432         for (const auto &lksPath : lks) {
433             if (link(filePath.data(), lksPath.data())) {
434                 HILOGE("failed to create hard link file %{public}s  errno : %{public}d", lksPath.c_str(), errno);
435             }
436         }
437 
438         struct timespec tv[2] = {sta.st_atim, sta.st_mtim};
439         UniqueFd fd(open(filePath.data(), O_RDONLY));
440         if (futimens(fd.Get(), tv) != 0) {
441             HILOGI("failed to change the file time. %{public}s , %{public}d", filePath.c_str(), errno);
442         }
443     }
444 }
445 
AsyncTaskRestore()446 void BackupExtExtension::AsyncTaskRestore()
447 {
448     auto task = [obj {wptr<BackupExtExtension>(this)}, tars {tars_}]() {
449         auto ptr = obj.promote();
450         BExcepUltils::BAssert(ptr, BError::Codes::EXT_BROKEN_FRAMEWORK,
451                               "Ext extension handle have been already released");
452 
453         try {
454             if (!IsAllFileReceived(tars)) {
455                 return;
456             }
457 
458             // 解压
459             int ret = ERR_OK;
460             for (auto item : tars) {
461                 if (ExtractFileExt(item) == "tar") {
462                     ret = ptr->DoRestore(item);
463                 }
464             }
465             // 恢复大文件
466             RestoreBigFiles();
467 
468             if (ret == ERR_OK) {
469                 HILOGI("after extra, do restore.");
470                 BExcepUltils::BAssert(ptr->extension_, BError::Codes::EXT_INVAL_ARG,
471                                       "extension handle have been already released");
472                 ret = ptr->extension_->OnRestore();
473             }
474 
475             // 处理返回结果
476             ptr->AppDone(ret);
477         } catch (const BError &e) {
478             ptr->AppDone(e.GetCode());
479         } catch (const exception &e) {
480             HILOGE("Catched an unexpected low-level exception %{public}s", e.what());
481             ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode());
482         } catch (...) {
483             HILOGE("Failed to restore the ext bundle");
484             ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode());
485         }
486         // 清空恢复目录
487         ptr->DoClear();
488     };
489 
490     // REM: 这里异步化了,需要做并发控制
491     // 在往线程池中投入任务之前将需要的数据拷贝副本到参数中,保证不发生读写竞争,
492     // 由于拷贝参数时尚运行在主线程中,故在参数拷贝过程中是线程安全的。
493     threadPool_.AddTask([task]() {
494         try {
495             task();
496         } catch (...) {
497             HILOGE("Failed to add task to thread pool");
498         }
499     });
500 }
501 
AsyncTaskRestoreForUpgrade()502 void BackupExtExtension::AsyncTaskRestoreForUpgrade()
503 {
504     auto task = [obj {wptr<BackupExtExtension>(this)}]() {
505         auto ptr = obj.promote();
506         BExcepUltils::BAssert(ptr, BError::Codes::EXT_BROKEN_FRAMEWORK,
507                               "Ext extension handle have been already released");
508         try {
509             BExcepUltils::BAssert(ptr->extension_, BError::Codes::EXT_INVAL_ARG,
510                                   "extension handle have been already released");
511             ptr->AppDone(ptr->extension_->OnRestore());
512         } catch (const BError &e) {
513             ptr->AppDone(e.GetCode());
514         } catch (const exception &e) {
515             HILOGE("Catched an unexpected low-level exception %{public}s", e.what());
516             ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode());
517         } catch (...) {
518             HILOGE("Failed to restore the ext bundle");
519             ptr->AppDone(BError(BError::Codes::EXT_INVAL_ARG).GetCode());
520         }
521         // 清空恢复目录
522         ptr->DoClear();
523     };
524 
525     // REM: 这里异步化了,需要做并发控制
526     // 在往线程池中投入任务之前将需要的数据拷贝副本到参数中,保证不发生读写竞争,
527     // 由于拷贝参数时尚运行在主线程中,故在参数拷贝过程中是线程安全的。
528     threadPool_.AddTask([task]() {
529         try {
530             task();
531         } catch (...) {
532             HILOGE("Failed to add task to thread pool");
533         }
534     });
535 }
536 
DoClear()537 void BackupExtExtension::DoClear()
538 {
539     try {
540         string backupCache = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_BACKUP);
541         string restoreCache = string(BConstants::PATH_BUNDLE_BACKUP_HOME).append(BConstants::SA_BUNDLE_BACKUP_RESTORE);
542 
543         if (!ForceRemoveDirectory(backupCache)) {
544             HILOGI("Failed to delete the backup cache %{public}s", backupCache.c_str());
545         }
546 
547         if (!ForceRemoveDirectory(restoreCache)) {
548             HILOGI("Failed to delete the restore cache %{public}s", restoreCache.c_str());
549         }
550         unique_lock<shared_mutex> lock(lock_);
551         tars_.clear();
552     } catch (...) {
553         HILOGI("Failed to clear");
554     }
555 }
556 
AppDone(ErrCode errCode)557 void BackupExtExtension::AppDone(ErrCode errCode)
558 {
559     auto proxy = ServiceProxy::GetInstance();
560     BExcepUltils::BAssert(proxy, BError::Codes::EXT_BROKEN_IPC, "Failed to obtain the ServiceProxy handle");
561     auto ret = proxy->AppDone(errCode);
562     if (ret != ERR_OK) {
563         HILOGE("Failed to notify the app done. err = %{public}d", ret);
564     }
565 }
566 } // namespace OHOS::FileManagement::Backup
567