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