1 /*
2 * Copyright (c) 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 "module_update.h"
17
18 #include <chrono>
19 #include <sys/mount.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <thread>
23
24 #include "directory_ex.h"
25 #include "log/log.h"
26 #include "module_constants.h"
27 #include "module_dm.h"
28 #include "module_error_code.h"
29 #include "module_file_repository.h"
30 #include "module_loop.h"
31 #include "module_update_task.h"
32 #include "module_utils.h"
33 #include "parse_util.h"
34 #include "scope_guard.h"
35 #include "string_ex.h"
36 #include "utils.h"
37
38 namespace OHOS {
39 namespace SysInstaller {
40 using namespace Updater;
41 using std::string;
42
43 namespace {
44 constexpr mode_t MOUNT_POINT_MODE = 0755;
45 constexpr int32_t LOOP_DEVICE_SETUP_ATTEMPTS = 3;
46
CreateLoopDevice(const string & path,const ImageStat & imageStat,Loop::LoopbackDeviceUniqueFd & loopbackDevice)47 bool CreateLoopDevice(const string &path, const ImageStat &imageStat, Loop::LoopbackDeviceUniqueFd &loopbackDevice)
48 {
49 for (int32_t attempts = 1; attempts <= LOOP_DEVICE_SETUP_ATTEMPTS; ++attempts) {
50 std::unique_ptr<Loop::LoopbackDeviceUniqueFd> device =
51 Loop::CreateLoopDevice(path, imageStat.imageOffset, imageStat.imageSize);
52 if (device != nullptr) {
53 loopbackDevice = std::move(*device);
54 break;
55 }
56 }
57 return loopbackDevice.deviceFd.Get() != -1;
58 }
59
StageUpdateModulePackage(const string & updatePath,const string & stagePath)60 bool StageUpdateModulePackage(const string &updatePath, const string &stagePath)
61 {
62 int ret = 0;
63 if (CheckPathExists(stagePath)) {
64 LOG(INFO) << stagePath << " already exists. Deleting";
65 ret = unlink(stagePath.c_str());
66 if (ret != 0) {
67 LOG(ERROR) << "Failed to unlink " << stagePath;
68 return false;
69 }
70 }
71 string path = ExtractFilePath(stagePath);
72 // active dir should create by module_update_sa
73 if (!CheckPathExists(path)) {
74 LOG(ERROR) << path << " doesn't exist.";
75 return false;
76 }
77 ret = link(updatePath.c_str(), stagePath.c_str());
78 if (ret != 0) {
79 LOG(ERROR) << "Unable to link " << updatePath << " to " << stagePath;
80 return false;
81 }
82
83 // stage other files
84 std::vector<std::string> files;
85 if (Updater::Utils::GetFilesFromDirectory(updatePath.substr(0, updatePath.rfind("/")), files, true) <= 0) {
86 LOG(ERROR) << "Failed to get files form " << updatePath;
87 return false;
88 }
89 for (const auto &file : files) {
90 std::string targetFile = path + ExtractFileName(file);
91 (void)unlink(targetFile.c_str());
92 ret = link(file.c_str(), targetFile.c_str());
93 if (ret != 0) {
94 LOG(ERROR) << "Unable to link " << file << " to " << targetFile;
95 return false;
96 }
97 }
98 LOG(INFO) << "success to link " << updatePath << " to " << stagePath;
99 return true;
100 }
101
CheckModulePackage(const std::string & mountPoint,const ModuleFile & moduleFile)102 bool CheckModulePackage(const std::string &mountPoint, const ModuleFile &moduleFile)
103 {
104 if (!IsEmptyFolder(mountPoint)) {
105 LOG(ERROR) << mountPoint << " is not empty";
106 return false;
107 }
108 if (!moduleFile.GetImageStat().has_value()) {
109 LOG(ERROR) << "Could not mount empty module package " << moduleFile.GetPath();
110 return false;
111 }
112 return true;
113 }
114 }
115
GetInstance()116 ModuleUpdate &ModuleUpdate::GetInstance()
117 {
118 static ModuleUpdate instance;
119 return instance;
120 }
121
RemoveMountPoint(const string & hmpName)122 bool ModuleUpdate::RemoveMountPoint(const string &hmpName)
123 {
124 string mountPoint = string(MODULE_ROOT_DIR) + "/" + hmpName;
125 LOG(INFO) << "Remove old mountpoint " << mountPoint;
126 if (!CheckPathExists(mountPoint)) {
127 LOG(INFO) << mountPoint << " doesn't exist.";
128 return true;
129 }
130
131 std::string prefixes[] = {MODULE_PREINSTALL_DIR, UPDATE_ACTIVE_DIR};
132 int ret = -1;
133 for (auto prefix : prefixes) {
134 std::string imagePath = prefix + "/" + hmpName + "/" + IMG_FILE_NAME;
135 if (Loop::RemoveDmLoopDevice(mountPoint, imagePath)) {
136 ret = 0;
137 LOG(INFO) << "Successful remove dm loop device, mountPoint=" << mountPoint
138 << ", imagePath=" << imagePath;
139 break;
140 }
141 }
142 if (ret != 0) {
143 LOG(ERROR) << "Fail remove dm loop device, mountPoint=" << mountPoint;
144 return false;
145 }
146 ret = rmdir(mountPoint.c_str());
147 if (ret != 0) {
148 LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
149 return false;
150 }
151 LOG(INFO) << "Successful remove mountPoint, hmpName: " << hmpName;
152 return true;
153 }
154
GetLatestUpdateModulePackage(const string & hmpName)155 std::unique_ptr<ModuleFile> ModuleUpdate::GetLatestUpdateModulePackage(const string &hmpName)
156 {
157 std::unique_ptr<ModuleFile> activeModuleFile = repository_.GetModuleFile(UPDATE_ACTIVE_DIR, hmpName);
158 std::unique_ptr<ModuleFile> updateModuleFile = repository_.GetModuleFile(UPDATE_INSTALL_DIR, hmpName);
159 std::unique_ptr<ModuleFile> ret = nullptr;
160 if (updateModuleFile != nullptr) {
161 if (activeModuleFile == nullptr || ModuleFile::CompareVersion(*updateModuleFile, *activeModuleFile)) {
162 string updatePath = updateModuleFile->GetPath();
163 string activePath = UPDATE_ACTIVE_DIR +
164 updatePath.substr(strlen(UPDATE_INSTALL_DIR), updatePath.length());
165 if (!StageUpdateModulePackage(updatePath, activePath)) {
166 return ret;
167 }
168 updateModuleFile->SetPath(activePath);
169 ret = std::move(updateModuleFile);
170 LOG(INFO) << "add updateModuleFile " << updatePath;
171 }
172 }
173 if (ret == nullptr && activeModuleFile != nullptr) {
174 LOG(INFO) << "add activeModuleFile " << activeModuleFile->GetPath();
175 ret = std::move(activeModuleFile);
176 }
177 return ret;
178 }
179
CheckMountComplete(const string & hmpName) const180 bool ModuleUpdate::CheckMountComplete(const string &hmpName) const
181 {
182 string path = std::string(MODULE_ROOT_DIR) + "/" + hmpName;
183 return CheckPathExists(path);
184 }
185
ProcessHmpFile(const string & hmpFile,const ModuleUpdateStatus & status,const Timer & timer)186 void ModuleUpdate::ProcessHmpFile(const string &hmpFile, const ModuleUpdateStatus &status, const Timer &timer)
187 {
188 LOG(INFO) << "process hmp file=" << hmpFile;
189 std::unique_ptr<ModuleFile> moduleFile = ModuleFile::Open(hmpFile);
190 if (moduleFile == nullptr) {
191 LOG(ERROR) << "Process hmp file fail, module file is null";
192 return;
193 }
194 HmpInstallType type = moduleFile->GetHmpPackageType();
195 if (CheckBootComplete() && IsHotHmpPackage(static_cast<int32_t>(type))) {
196 if (type == HOT_SA_TYPE && IsRunning(moduleFile->GetVersionInfo().saInfoList.front().saId)) {
197 LOG(INFO) << "ondemand sa is running, saId=" << moduleFile->GetVersionInfo().saInfoList.front().saId;
198 return;
199 }
200 if (type == HOT_APP_TYPE) {
201 KillProcessOnArkWeb();
202 }
203 if (!RemoveMountPoint(status.hmpName)) {
204 return;
205 }
206 } else if (CheckMountComplete(status.hmpName)) {
207 LOG(INFO) << "Check mount complete, hmpName=" << status.hmpName;
208 return;
209 }
210 repository_.InitRepository(status.hmpName, timer);
211 PrepareModuleFileList(status);
212 }
213
DoModuleUpdate(ModuleUpdateStatus & status)214 bool ModuleUpdate::DoModuleUpdate(ModuleUpdateStatus &status)
215 {
216 LOG(INFO) << "enter domoduleupdate";
217 Timer timer;
218 std::string hmpPackagePath = std::string(MODULE_PREINSTALL_DIR) + "/" + status.hmpName;
219 LOG(INFO) << "DoModuleUpdate hmp package path=" << hmpPackagePath;
220 std::vector<std::string> files;
221 GetDirFiles(hmpPackagePath, files);
222 ON_SCOPE_EXIT(clear) {
223 repository_.Clear();
224 moduleFileList_.clear();
225 };
226 for (auto &file : files) {
227 std::string hmpPackage = GetFileName(file);
228 if (!CheckFileSuffix(file, MODULE_PACKAGE_SUFFIX) || hmpPackage.empty()) {
229 continue;
230 }
231 ProcessHmpFile(file, status, timer);
232 }
233 if (moduleFileList_.size() != 1) {
234 LOG(INFO) << status.hmpName << " module size is invalid: " << moduleFileList_.size();
235 return false;
236 }
237 if (!Loop::PreAllocateLoopDevices(moduleFileList_.size())) {
238 LOG(ERROR) << "Failed to pre allocate loop devices, hmp package name=" << status.hmpName;
239 return false;
240 }
241 if (!ActivateModules(status, timer)) {
242 LOG(ERROR) << "Failed to activate modules, hmp package name=" << status.hmpName;
243 return false;
244 }
245 LOG(INFO) << "Success to activate modules, hmp package name=" << status.hmpName;
246 return true;
247 }
248
CheckModuleUpdate()249 void ModuleUpdate::CheckModuleUpdate()
250 {
251 LOG(INFO) << "CheckModuleUpdate begin";
252 Timer timer;
253 std::vector<std::string> files;
254 std::unordered_set<std::string> hmpNameSet;
255 GetDirFiles(MODULE_PREINSTALL_DIR, files);
256 for (auto &file : files) {
257 if (!CheckFileSuffix(file, MODULE_PACKAGE_SUFFIX)) {
258 continue;
259 }
260 std::unique_ptr<ModuleFile> moduleFile = ModuleFile::Open(file);
261 if (moduleFile == nullptr) {
262 continue;
263 }
264 std::string hmpName = GetHmpName(file);
265 if (hmpName.empty()) {
266 continue;
267 }
268 hmpNameSet.emplace(hmpName);
269 }
270 auto &instance = ModuleUpdateTaskManager::GetInstance();
271 instance.Start();
272 ON_SCOPE_EXIT(clear) {
273 instance.ClearTask();
274 instance.Stop();
275 LOG(INFO) << "CheckModuleUpdate done, duration=" << timer;
276 if (!ForceRemoveDirectory(UPDATE_INSTALL_DIR)) {
277 LOG(ERROR) << "Failed to remove " << UPDATE_INSTALL_DIR << " err=" << errno;
278 }
279 };
280 for (auto &hmpName : hmpNameSet) {
281 instance.AddTask(hmpName);
282 }
283 while (instance.GetCurTaskNum() != 0) {
284 sleep(1);
285 }
286 }
287
PrepareModuleFileList(const ModuleUpdateStatus & status)288 void ModuleUpdate::PrepareModuleFileList(const ModuleUpdateStatus &status)
289 {
290 std::unique_ptr<ModuleFile> systemModuleFile = repository_.GetModuleFile(MODULE_PREINSTALL_DIR, status.hmpName);
291 if (systemModuleFile == nullptr) {
292 LOG(ERROR) << "Failed to get preinstalled hmp " << status.hmpName;
293 return;
294 }
295 std::unique_ptr<ModuleFile> latestModuleFile = GetLatestUpdateModulePackage(status.hmpName);
296 if (latestModuleFile != nullptr && ModuleFile::CompareVersion(*latestModuleFile, *systemModuleFile)) {
297 moduleFileList_.emplace_back(std::move(*latestModuleFile));
298 } else {
299 moduleFileList_.emplace_back(std::move(*systemModuleFile));
300 // when choose preInstall hmp, remove activeHmp and backupHmp
301 RemoveSpecifiedDir(std::string(UPDATE_ACTIVE_DIR) + "/" + status.hmpName);
302 RemoveSpecifiedDir(std::string(UPDATE_BACKUP_DIR) + "/" + status.hmpName);
303 }
304 }
305
ActivateModules(ModuleUpdateStatus & status,const Timer & timer)306 bool ModuleUpdate::ActivateModules(ModuleUpdateStatus &status, const Timer &timer)
307 {
308 // size = 1
309 for (const auto &moduleFile : moduleFileList_) {
310 if (!moduleFile.GetImageStat().has_value()) {
311 LOG(INFO) << moduleFile.GetPath() << " is empty module package";
312 continue;
313 }
314 status.isPreInstalled = repository_.IsPreInstalledModule(moduleFile);
315 status.isHotInstall = IsHotHmpPackage(static_cast<int32_t>(moduleFile.GetHmpPackageType()));
316 status.isAllMountSuccess = MountModulePackage(moduleFile, !status.isPreInstalled);
317 if (!status.isAllMountSuccess) {
318 LOG(ERROR) << "Failed to mount module package " << moduleFile.GetPath();
319 repository_.SaveInstallerResult(moduleFile.GetPath(), status.hmpName,
320 ERR_INSTALL_FAIL, "mount fail", timer);
321 }
322 // bugfix: when sise = 1, for() find the second item
323 break;
324 }
325 ReportModuleUpdateStatus(status);
326 LOG(INFO) << "ActivateModule mount result:" << status.isAllMountSuccess << ", hmp package name:" << status.hmpName;
327 return status.isAllMountSuccess;
328 }
329
WaitDevice(const std::string & blockDevice) const330 void ModuleUpdate::WaitDevice(const std::string &blockDevice) const
331 {
332 const int waitTime = 150; // wait max 3s
333 int time = 0;
334 while (!CheckPathExists(blockDevice) && time++ < waitTime) {
335 usleep(20000); // 20000: 20ms
336 }
337 }
338
MountModulePackage(const ModuleFile & moduleFile,const bool mountOnVerity) const339 bool ModuleUpdate::MountModulePackage(const ModuleFile &moduleFile, const bool mountOnVerity) const
340 {
341 string mountPoint = string(MODULE_ROOT_DIR) + "/" + moduleFile.GetVersionInfo().hmpName;
342 LOG(INFO) << "Creating mount point: " << mountPoint;
343 Timer timer;
344 int ret = 0;
345 if (!CreateDirIfNeeded(mountPoint, MOUNT_POINT_MODE)) {
346 LOG(ERROR) << "Could not create mount point " << mountPoint << " errno: " << errno;
347 return false;
348 }
349 ON_SCOPE_EXIT(rmDir) {
350 ret = rmdir(mountPoint.c_str());
351 if (ret != 0) {
352 LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
353 }
354 };
355 if (!CheckModulePackage(mountPoint, moduleFile)) {
356 return false;
357 }
358
359 const string &fullPath = ExtractFilePath(moduleFile.GetPath()) + IMG_FILE_NAME;
360 const ImageStat &imageStat = moduleFile.GetImageStat().value();
361 Loop::LoopbackDeviceUniqueFd loopbackDevice;
362 if (!CreateLoopDevice(fullPath, imageStat, loopbackDevice)) {
363 LOG(ERROR) << "Could not create loop device for " << fullPath;
364 return false;
365 }
366 LOG(INFO) << "Loopback device created: " << loopbackDevice.name << " fsType=" << imageStat.fsType;
367 string blockDevice = loopbackDevice.name;
368 if (mountOnVerity) {
369 if (!CreateDmDevice(moduleFile, blockDevice)) {
370 LOG(ERROR) << "Could not create dm-verity device on " << blockDevice;
371 Loop::ClearDmLoopDevice(blockDevice, false);
372 return false;
373 }
374 }
375 WaitDevice(blockDevice);
376 uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
377 ret = mount(blockDevice.c_str(), mountPoint.c_str(), imageStat.fsType, mountFlags, nullptr);
378 if (ret != 0) {
379 LOG(ERROR) << "Mounting failed for module package " << fullPath << " errno:" << errno;
380 Loop::ClearDmLoopDevice(blockDevice, true);
381 return false;
382 }
383 LOG(INFO) << "Successfully mounted module package " << fullPath << " on " << mountPoint << " duration=" << timer;
384 loopbackDevice.CloseGood();
385 CANCEL_SCOPE_EXIT_GUARD(rmDir);
386 return true;
387 }
388
ReportModuleUpdateStatus(const ModuleUpdateStatus & status) const389 void ModuleUpdate::ReportModuleUpdateStatus(const ModuleUpdateStatus &status) const
390 {
391 auto &instance = ModuleUpdateTaskManager::GetInstance();
392 if (!instance.GetTaskResult()) {
393 LOG(ERROR) << "ReportModuleUpdateStatus, module update fail";
394 instance.ClearTask();
395 }
396 if (!status.isAllMountSuccess) {
397 LOG(ERROR) << "ReportModuleUpdateStatus mount fail, hmp name=" << status.hmpName;
398 RemoveSpecifiedDir(std::string(UPDATE_INSTALL_DIR) + "/" + status.hmpName);
399 Revert(status.hmpName, !status.isHotInstall);
400 return;
401 }
402 LOG(INFO) << "ReportModuleUpdateStatus mount success, hmp name=" << status.hmpName;
403 }
404 } // namespace SysInstaller
405 } // namespace OHOS
406