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 return true;
83 }
84
GetLatestUpdateModulePackage(const int32_t saId)85 std::unique_ptr<ModuleFile> GetLatestUpdateModulePackage(const int32_t saId)
86 {
87 auto &instance = ModuleFileRepository::GetInstance();
88 std::unique_ptr<ModuleFile> activeModuleFile = instance.GetModuleFile(UPDATE_ACTIVE_DIR, saId);
89 std::unique_ptr<ModuleFile> updateModuleFile = instance.GetModuleFile(UPDATE_INSTALL_DIR, saId);
90 std::unique_ptr<ModuleFile> ret = nullptr;
91 if (updateModuleFile != nullptr) {
92 if (activeModuleFile == nullptr || ModuleFile::CompareVersion(*updateModuleFile, *activeModuleFile)) {
93 string updatePath = updateModuleFile->GetPath();
94 string activePath = UPDATE_ACTIVE_DIR +
95 updatePath.substr(strlen(UPDATE_INSTALL_DIR), updatePath.length());
96 if (!StageUpdateModulePackage(updatePath, activePath)) {
97 return ret;
98 }
99 updateModuleFile->SetPath(activePath);
100 ret = std::move(updateModuleFile);
101 }
102 }
103 if (ret == nullptr && activeModuleFile != nullptr) {
104 ret = std::move(activeModuleFile);
105 }
106 return ret;
107 }
108
GetSuccessSaIdString(const ModuleUpdateStatus & status)109 string GetSuccessSaIdString(const ModuleUpdateStatus &status)
110 {
111 string ret = "";
112 for (const SaStatus &saStatus : status.saStatusList) {
113 if (saStatus.isMountSuccess) {
114 ret += std::to_string(saStatus.saId) + " ";
115 }
116 }
117 return ret;
118 }
119 }
120
CheckModuleUpdate(const string & path)121 string ModuleUpdate::CheckModuleUpdate(const string &path)
122 {
123 LOG(INFO) << "CheckModuleUpdate path=" << path;
124 Timer timer;
125 string ret = "";
126 if (!ParseSaProfiles(path)) {
127 LOG(ERROR) << "Failed to parse sa profile";
128 return ret;
129 }
130 ModuleFileRepository::GetInstance().InitRepository(saIdSet_);
131 ON_SCOPE_EXIT(clear) {
132 ModuleFileRepository::GetInstance().Clear();
133 };
134 PrepareModuleFileList();
135 if (moduleFileList_.empty()) {
136 LOG(INFO) << "No module needs to activate";
137 return ret;
138 }
139 if (!Loop::PreAllocateLoopDevices(moduleFileList_.size())) {
140 LOG(ERROR) << "Failed to pre allocate loop devices";
141 return ret;
142 }
143 if (!ActivateModules()) {
144 LOG(ERROR) << "Failed to activate modules";
145 return ret;
146 }
147 ret = GetSuccessSaIdString(status_);
148 LOG(INFO) << "CheckModuleUpdate done, duration=" << timer << ", ret=" << ret;
149 return ret;
150 }
151
ParseSaProfiles(const string & path)152 bool ModuleUpdate::ParseSaProfiles(const string &path)
153 {
154 string realProfilePath = "";
155 if (!PathToRealPath(path, realProfilePath)) {
156 LOG(ERROR) << "Failed to get real path " << path;
157 return false;
158 }
159 ParseUtil parser;
160 if (!parser.ParseSaProfiles(realProfilePath)) {
161 LOG(ERROR) << "ParseSaProfiles failed! path=" << realProfilePath;
162 return false;
163 }
164 status_.process = Str16ToStr8(parser.GetProcessName());
165 auto saInfos = parser.GetAllSaProfiles();
166 for (const auto &saInfo : saInfos) {
167 saIdSet_.insert(saInfo.saId);
168 }
169 return true;
170 }
171
PrepareModuleFileList()172 void ModuleUpdate::PrepareModuleFileList()
173 {
174 for (int32_t saId : saIdSet_) {
175 auto &instance = ModuleFileRepository::GetInstance();
176 std::unique_ptr<ModuleFile> systemModuleFile = instance.GetModuleFile(MODULE_PREINSTALL_DIR, saId);
177 if (systemModuleFile == nullptr) {
178 LOG(ERROR) << "Failed to get preinstalled sa " << saId;
179 continue;
180 }
181 std::unique_ptr<ModuleFile> latestModuleFile = GetLatestUpdateModulePackage(saId);
182 if (latestModuleFile != nullptr && ModuleFile::CompareVersion(*latestModuleFile, *systemModuleFile)) {
183 moduleFileList_.emplace_back(std::move(*latestModuleFile));
184 } else {
185 moduleFileList_.emplace_back(std::move(*systemModuleFile));
186 }
187 }
188 }
189
ActivateModules()190 bool ModuleUpdate::ActivateModules()
191 {
192 bool activateSuccess = true;
193 for (const auto &moduleFile : moduleFileList_) {
194 if (!moduleFile.GetImageStat().has_value()) {
195 LOG(INFO) << moduleFile.GetPath() << " is empty module package";
196 continue;
197 }
198 SaStatus saStatus;
199 saStatus.saId = moduleFile.GetSaId();
200 saStatus.isPreInstalled = ModuleFileRepository::GetInstance().IsPreInstalledModule(moduleFile);
201 saStatus.isMountSuccess = MountModulePackage(moduleFile, !saStatus.isPreInstalled);
202 if (!saStatus.isMountSuccess) {
203 LOG(ERROR) << "Failed to mount module package " << moduleFile.GetPath();
204 activateSuccess = false;
205 }
206 status_.saStatusList.emplace_back(std::move(saStatus));
207 }
208 ReportMountStatus(status_);
209 return activateSuccess;
210 }
211
MountModulePackage(const ModuleFile & moduleFile,const bool mountOnVerity) const212 bool ModuleUpdate::MountModulePackage(const ModuleFile &moduleFile, const bool mountOnVerity) const
213 {
214 string mountPoint = string(MODULE_ROOT_DIR) + "/" + std::to_string(moduleFile.GetSaId());
215 LOG(INFO) << "Creating mount point: " << mountPoint;
216 Timer timer;
217 int ret = 0;
218 if (!CreateDirIfNeeded(mountPoint, MOUNT_POINT_MODE)) {
219 LOG(ERROR) << "Could not create mount point " << mountPoint << " errno: " << errno;
220 return false;
221 }
222 ON_SCOPE_EXIT(rmDir) {
223 ret = rmdir(mountPoint.c_str());
224 if (ret != 0) {
225 LOG(WARNING) << "Could not rmdir " << mountPoint << " errno: " << errno;
226 }
227 };
228 if (!IsEmptyFolder(mountPoint)) {
229 LOG(ERROR) << mountPoint << " is not empty";
230 return false;
231 }
232 const string &fullPath = moduleFile.GetPath();
233 if (!moduleFile.GetImageStat().has_value()) {
234 LOG(ERROR) << "Could not mount empty module package " << moduleFile.GetPath();
235 return false;
236 }
237 const ImageStat &imageStat = moduleFile.GetImageStat().value();
238 Loop::LoopbackDeviceUniqueFd loopbackDevice;
239 if (!CreateLoopDevice(fullPath, imageStat, loopbackDevice)) {
240 LOG(ERROR) << "Could not create loop device for " << fullPath;
241 return false;
242 }
243 LOG(INFO) << "Loopback device created: " << loopbackDevice.name;
244
245 string blockDevice = loopbackDevice.name;
246 if (mountOnVerity) {
247 if (!CreateDmDevice(moduleFile, blockDevice)) {
248 LOG(ERROR) << "Could not create dm-verity device on " << blockDevice;
249 return false;
250 }
251 }
252 uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
253 LOG(INFO) << "fsType=" << imageStat.fsType;
254 ret = mount(blockDevice.c_str(), mountPoint.c_str(), imageStat.fsType, mountFlags, nullptr);
255 if (ret != 0) {
256 LOG(ERROR) << "Mounting failed for module package " << fullPath << " errno:" << errno;
257 return false;
258 }
259 LOG(INFO) << "Successfully mounted module package " << fullPath << " on " << mountPoint << " duration=" << timer;
260 loopbackDevice.CloseGood();
261 CANCEL_SCOPE_EXIT_GUARD(rmDir);
262 return true;
263 }
264
ReportMountStatus(const ModuleUpdateStatus & status) const265 void ModuleUpdate::ReportMountStatus(const ModuleUpdateStatus &status) const
266 {
267 int32_t times = RETRY_TIMES_FOR_MODULE_UPDATE_SERVICE;
268 constexpr int32_t duration = std::chrono::microseconds(MILLISECONDS_WAITING_MODULE_UPDATE_SERVICE).count();
269 while (times > 0) {
270 times--;
271 int32_t ret = ModuleUpdateKits::GetInstance().ReportModuleUpdateStatus(status);
272 if (ret == ModuleErrorCode::ERR_SERVICE_NOT_FOUND) {
273 LOG(INFO) << "retry to report mount failed";
274 usleep(duration);
275 } else {
276 if (ret != ModuleErrorCode::MODULE_UPDATE_SUCCESS) {
277 LOG(ERROR) << "Failed to report mount failed";
278 }
279 return;
280 }
281 }
282 LOG(ERROR) << "Report mount failed timeout";
283 }
284 } // namespace SysInstaller
285 } // namespace OHOS