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
23 #include "directory_ex.h"
24 #include "log/log.h"
25 #include "module_constants.h"
26 #include "module_dm.h"
27 #include "module_error_code.h"
28 #include "module_file_repository.h"
29 #include "module_loop.h"
30 #include "module_update_kits.h"
31 #include "module_utils.h"
32 #include "parse_util.h"
33 #include "scope_guard.h"
34 #include "string_ex.h"
35 #include "utils.h"
36
37 namespace OHOS {
38 namespace SysInstaller {
39 using namespace Updater;
40 using std::string;
41
42 namespace {
43 constexpr mode_t MOUNT_POINT_MODE = 0755;
44 constexpr int32_t LOOP_DEVICE_SETUP_ATTEMPTS = 3;
45 constexpr int32_t RETRY_TIMES_FOR_MODULE_UPDATE_SERVICE = 10;
46 constexpr std::chrono::milliseconds MILLISECONDS_WAITING_MODULE_UPDATE_SERVICE(100);
47
CreateLoopDevice(const string & path,const ImageStat & imageStat,Loop::LoopbackDeviceUniqueFd & loopbackDevice)48 bool CreateLoopDevice(const string &path, const ImageStat &imageStat, Loop::LoopbackDeviceUniqueFd &loopbackDevice)
49 {
50 for (int32_t attempts = 1; attempts <= LOOP_DEVICE_SETUP_ATTEMPTS; ++attempts) {
51 std::unique_ptr<Loop::LoopbackDeviceUniqueFd> device =
52 Loop::CreateLoopDevice(path, imageStat.imageOffset, imageStat.imageSize);
53 if (device != nullptr) {
54 loopbackDevice = std::move(*device);
55 break;
56 }
57 }
58 return loopbackDevice.deviceFd.Get() != -1;
59 }
60
StageUpdateModulePackage(const string & updatePath,const string & stagePath)61 bool StageUpdateModulePackage(const string &updatePath, const string &stagePath)
62 {
63 int ret = 0;
64 if (CheckPathExists(stagePath)) {
65 LOG(INFO) << stagePath << " already exists. Deleting";
66 ret = unlink(stagePath.c_str());
67 if (ret != 0) {
68 LOG(ERROR) << "Failed to unlink " << stagePath;
69 return false;
70 }
71 }
72 string path = ExtractFilePath(stagePath);
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
GetLatestUpdateModulePackage(const int32_t saId)102 std::unique_ptr<ModuleFile> GetLatestUpdateModulePackage(const int32_t saId)
103 {
104 auto &instance = ModuleFileRepository::GetInstance();
105 std::unique_ptr<ModuleFile> activeModuleFile = instance.GetModuleFile(UPDATE_ACTIVE_DIR, saId);
106 std::unique_ptr<ModuleFile> updateModuleFile = instance.GetModuleFile(UPDATE_INSTALL_DIR, saId);
107 std::unique_ptr<ModuleFile> ret = nullptr;
108 if (updateModuleFile != nullptr) {
109 if (activeModuleFile == nullptr || ModuleFile::CompareVersion(*updateModuleFile, *activeModuleFile)) {
110 string updatePath = updateModuleFile->GetPath();
111 string activePath = UPDATE_ACTIVE_DIR +
112 updatePath.substr(strlen(UPDATE_INSTALL_DIR), updatePath.length());
113 if (!StageUpdateModulePackage(updatePath, activePath)) {
114 return ret;
115 }
116 updateModuleFile->SetPath(activePath);
117 ret = std::move(updateModuleFile);
118 LOG(INFO) << "add updateModuleFile " << updatePath;
119 }
120 }
121 if (ret == nullptr && activeModuleFile != nullptr) {
122 LOG(INFO) << "add activeModuleFile " << activeModuleFile->GetPath();
123 ret = std::move(activeModuleFile);
124 }
125 return ret;
126 }
127
GetSuccessSaIdString(const ModuleUpdateStatus & status)128 string GetSuccessSaIdString(const ModuleUpdateStatus &status)
129 {
130 string ret = "";
131 for (const SaStatus &saStatus : status.saStatusList) {
132 if (saStatus.isMountSuccess) {
133 ret += std::to_string(saStatus.saId) + " ";
134 }
135 }
136 return ret;
137 }
138 }
139
CheckMountComplete(string & status) const140 bool ModuleUpdate::CheckMountComplete(string &status) const
141 {
142 bool mountStatus = false;
143 for (int32_t saId : saIdSet_) {
144 string path = std::string(MODULE_ROOT_DIR) + "/" + std::to_string(saId);
145 if (CheckPathExists(path)) {
146 mountStatus = true;
147 status += std::to_string(saId) + " ";
148 }
149 }
150 return mountStatus;
151 }
152
CheckModuleUpdate(const string & path)153 string ModuleUpdate::CheckModuleUpdate(const string &path)
154 {
155 LOG(INFO) << "CheckModuleUpdate path=" << path;
156 Timer timer;
157 string ret = "";
158 if (!ParseSaProfiles(path)) {
159 LOG(ERROR) << "Failed to parse sa profile";
160 return ret;
161 }
162 if (CheckMountComplete(ret)) {
163 LOG(INFO) << "CheckMountComplete ret=" << ret;
164 return ret;
165 }
166
167 ModuleFileRepository::GetInstance().InitRepository(saIdSet_);
168 ON_SCOPE_EXIT(clear) {
169 ModuleFileRepository::GetInstance().Clear();
170 };
171 PrepareModuleFileList();
172 if (moduleFileList_.empty()) {
173 LOG(INFO) << "No module needs to activate";
174 return ret;
175 }
176 if (!Loop::PreAllocateLoopDevices(moduleFileList_.size())) {
177 LOG(ERROR) << "Failed to pre allocate loop devices";
178 return ret;
179 }
180 if (!ActivateModules()) {
181 LOG(ERROR) << "Failed to activate modules";
182 return ret;
183 }
184 ret = GetSuccessSaIdString(status_);
185 LOG(INFO) << "CheckModuleUpdate done, duration=" << timer << ", ret=" << ret;
186 return ret;
187 }
188
ParseSaProfiles(const string & path)189 bool ModuleUpdate::ParseSaProfiles(const string &path)
190 {
191 string realProfilePath = "";
192 if (!PathToRealPath(path, realProfilePath)) {
193 LOG(ERROR) << "Failed to get real path " << path;
194 return false;
195 }
196 ParseUtil parser;
197 if (!parser.ParseSaProfiles(realProfilePath)) {
198 LOG(ERROR) << "ParseSaProfiles failed! path=" << realProfilePath;
199 return false;
200 }
201 status_.process = Str16ToStr8(parser.GetProcessName());
202 auto saInfos = parser.GetAllSaProfiles();
203 for (const auto &saInfo : saInfos) {
204 saIdSet_.insert(saInfo.saId);
205 }
206 return true;
207 }
208
PrepareModuleFileList()209 void ModuleUpdate::PrepareModuleFileList()
210 {
211 for (int32_t saId : saIdSet_) {
212 auto &instance = ModuleFileRepository::GetInstance();
213 std::unique_ptr<ModuleFile> systemModuleFile = instance.GetModuleFile(MODULE_PREINSTALL_DIR, saId);
214 if (systemModuleFile == nullptr) {
215 LOG(ERROR) << "Failed to get preinstalled sa " << saId;
216 continue;
217 }
218 std::unique_ptr<ModuleFile> latestModuleFile = GetLatestUpdateModulePackage(saId);
219 if (latestModuleFile != nullptr && ModuleFile::CompareVersion(*latestModuleFile, *systemModuleFile)) {
220 moduleFileList_.emplace_back(std::move(*latestModuleFile));
221 } else {
222 moduleFileList_.emplace_back(std::move(*systemModuleFile));
223 }
224 }
225 }
226
ActivateModules()227 bool ModuleUpdate::ActivateModules()
228 {
229 bool activateSuccess = true;
230 for (const auto &moduleFile : moduleFileList_) {
231 if (!moduleFile.GetImageStat().has_value()) {
232 LOG(INFO) << moduleFile.GetPath() << " is empty module package";
233 continue;
234 }
235 SaStatus saStatus;
236 saStatus.saId = moduleFile.GetSaId();
237 saStatus.isPreInstalled = ModuleFileRepository::GetInstance().IsPreInstalledModule(moduleFile);
238 saStatus.isMountSuccess = MountModulePackage(moduleFile, !saStatus.isPreInstalled);
239 if (!saStatus.isMountSuccess) {
240 LOG(ERROR) << "Failed to mount module package " << moduleFile.GetPath();
241 activateSuccess = false;
242 ModuleFileRepository::GetInstance().SaveInstallerResult(moduleFile.GetPath(),
243 GetHmpName(moduleFile.GetPath()), ERR_INSTALL_FAIL, "mount fail");
244 }
245 status_.saStatusList.emplace_back(std::move(saStatus));
246 }
247 LOG(INFO) << "ActivateModules activateSuccess:" << activateSuccess;
248 ReportMountStatus(status_);
249 return activateSuccess;
250 }
251
WaitDevice(const std::string & blockDevice) const252 void ModuleUpdate::WaitDevice(const std::string &blockDevice) const
253 {
254 const int waitTime = 150; // wait max 3s
255 int time = 0;
256 while (!CheckPathExists(blockDevice) && time++ < waitTime) {
257 usleep(20000); // 20000: 20ms
258 }
259 }
260
MountModulePackage(const ModuleFile & moduleFile,const bool mountOnVerity) const261 bool ModuleUpdate::MountModulePackage(const ModuleFile &moduleFile, const bool mountOnVerity) const
262 {
263 string mountPoint = string(MODULE_ROOT_DIR) + "/" + std::to_string(moduleFile.GetSaId());
264 LOG(INFO) << "Creating mount point: " << mountPoint;
265 Timer timer;
266 int ret = 0;
267 if (!CreateDirIfNeeded(mountPoint, MOUNT_POINT_MODE)) {
268 LOG(ERROR) << "Could not create mount point " << mountPoint << " errno: " << errno;
269 return false;
270 }
271 ON_SCOPE_EXIT(rmDir) {
272 ret = rmdir(mountPoint.c_str());
273 if (ret != 0) {
274 LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
275 }
276 };
277 if (!IsEmptyFolder(mountPoint)) {
278 LOG(ERROR) << mountPoint << " is not empty";
279 return false;
280 }
281 const string &fullPath = moduleFile.GetPath();
282 if (!moduleFile.GetImageStat().has_value()) {
283 LOG(ERROR) << "Could not mount empty module package " << moduleFile.GetPath();
284 return false;
285 }
286 const ImageStat &imageStat = moduleFile.GetImageStat().value();
287 Loop::LoopbackDeviceUniqueFd loopbackDevice;
288 if (!CreateLoopDevice(fullPath, imageStat, loopbackDevice)) {
289 LOG(ERROR) << "Could not create loop device for " << fullPath;
290 return false;
291 }
292 LOG(INFO) << "Loopback device created: " << loopbackDevice.name << " fsType=" << imageStat.fsType;
293
294 string blockDevice = loopbackDevice.name;
295 if (mountOnVerity) {
296 if (!CreateDmDevice(moduleFile, blockDevice)) {
297 LOG(ERROR) << "Could not create dm-verity device on " << blockDevice;
298 return false;
299 }
300 }
301 WaitDevice(blockDevice);
302 uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
303 ret = mount(blockDevice.c_str(), mountPoint.c_str(), imageStat.fsType, mountFlags, nullptr);
304 if (ret != 0) {
305 LOG(ERROR) << "Mounting failed for module package " << fullPath << " errno:" << errno;
306 return false;
307 }
308 LOG(INFO) << "Successfully mounted module package " << fullPath << " on " << mountPoint << " duration=" << timer;
309 loopbackDevice.CloseGood();
310 CANCEL_SCOPE_EXIT_GUARD(rmDir);
311 return true;
312 }
313
ReportMountStatus(const ModuleUpdateStatus & status) const314 void ModuleUpdate::ReportMountStatus(const ModuleUpdateStatus &status) const
315 {
316 int32_t times = RETRY_TIMES_FOR_MODULE_UPDATE_SERVICE;
317 constexpr int32_t duration = std::chrono::microseconds(MILLISECONDS_WAITING_MODULE_UPDATE_SERVICE).count();
318 while (times > 0) {
319 times--;
320 int32_t ret = ModuleUpdateKits::GetInstance().ReportModuleUpdateStatus(status);
321 if (ret == ModuleErrorCode::ERR_SERVICE_NOT_FOUND) {
322 LOG(INFO) << "retry to report mount failed";
323 usleep(duration);
324 } else {
325 if (ret != ModuleErrorCode::MODULE_UPDATE_SUCCESS) {
326 LOG(ERROR) << "Failed to report mount failed";
327 }
328 return;
329 }
330 }
331 LOG(ERROR) << "Report mount failed timeout";
332 }
333 } // namespace SysInstaller
334 } // namespace OHOS
335