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_loop.h"
17 #include <dirent.h>
18 #include <fcntl.h>
19 #include <filesystem>
20 #include <libgen.h>
21 #include <mutex>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24 #include <sys/statfs.h>
25 #include <sys/mount.h>
26 #include <sys/types.h>
27 #include <vector>
28 #include <linux/fs.h>
29 #include <linux/loop.h>
30 #include <linux/magic.h>
31 #include <cerrno>
32 #include "securec.h"
33 #include "string_ex.h"
34 #include "log/log.h"
35 #include "module_dm.h"
36 #include "module_utils.h"
37 #include "module_constants.h"
38 #include "sys/sysmacros.h"
39 #include "scope_guard.h"
40
41 namespace OHOS {
42 namespace SysInstaller {
43 namespace Loop {
44 using namespace Updater;
45 using std::string;
46
47 namespace {
48 const int LOOP_LENGTH = 4;
49 const int LOOP_CTL_LENGTH = 12;
50 constexpr const char *LOOP_CTL_PATH = "/dev/loop-control";
51 constexpr const char *BLOCK_DEV_PATH = "/dev/block";
52 constexpr const char *LOOP_PREFIX = "loop";
53 constexpr const char *DEVICE_PREFIX = "/dev/";
54 constexpr const char *SYSTEM_BLOCK_PATH = "/sys/block/";
55 constexpr const char *READ_AHEAD_NAME = "/queue/read_ahead_kb";
56 constexpr const char *READ_AHEAD_KB = "128";
57 constexpr const char *MODULE_LOOP_PREFIX = "module:";
58 constexpr const char *LOOP_DEV_PATH = "/dev/loop";
59 constexpr const char *LOOP_BLOCK_PATH = "/dev/block/loop";
60 const size_t LOOP_DEVICE_RETRY_ATTEMPTS = 6u;
61 const uint32_t LOOP_BLOCK_SIZE = 4096;
62 const std::chrono::milliseconds WAIT_FOR_DEVICE_TIME(50);
63 const std::chrono::seconds WAIT_FOR_LOOP_TIME(50);
64 }
65
IsRealPath(std::string path)66 static bool IsRealPath(std::string path)
67 {
68 char buf[PATH_MAX] = { 0 };
69 if (realpath(path.c_str(), buf) == nullptr) {
70 return false;
71 }
72 std::string tmpPath = buf;
73 return (path == tmpPath);
74 }
75
MaybeCloseBad() const76 void LoopbackDeviceUniqueFd::MaybeCloseBad() const
77 {
78 if (deviceFd.Get() != -1) {
79 int ret = ioctl(deviceFd.Get(), LOOP_CLR_FD);
80 if (ret < 0) {
81 LOG(ERROR) << "Failed to clear fd for loopback device";
82 }
83 }
84 }
85
PreAllocateLoopDevices(const size_t num)86 bool PreAllocateLoopDevices(const size_t num)
87 {
88 if (!WaitForFile(LOOP_CTL_PATH, WAIT_FOR_LOOP_TIME)) {
89 LOG(ERROR) << "loop-control is not ready";
90 return false;
91 }
92 int fd = open(LOOP_CTL_PATH, O_RDWR | O_CLOEXEC);
93 if (fd == -1) {
94 LOG(ERROR) << "Failed to open loop-control";
95 return false;
96 }
97 UniqueFd ctlFd(fd);
98
99 bool found = false;
100 size_t startId = 0;
101 DIR *dir = opendir(BLOCK_DEV_PATH);
102 if (dir == nullptr) {
103 LOG(ERROR) << "Failed to open " << BLOCK_DEV_PATH;
104 return false;
105 }
106 struct dirent *ptr = nullptr;
107 while ((ptr = readdir(dir)) != nullptr) {
108 if (strncmp(ptr->d_name, LOOP_PREFIX, strlen(LOOP_PREFIX)) != 0) {
109 continue;
110 }
111 string idStr = ptr->d_name + strlen(LOOP_PREFIX);
112 int id = 0;
113 if (StrToInt(idStr, id)) {
114 size_t devId = static_cast<size_t>(id);
115 if (startId < devId) {
116 startId = devId;
117 found = true;
118 }
119 }
120 }
121 closedir(dir);
122 if (found) {
123 startId++;
124 }
125 LOG(INFO) << "start id is " << startId;
126
127 for (size_t id = startId; id < num + startId; ++id) {
128 int ret = ioctl(ctlFd.Get(), LOOP_CTL_ADD, id);
129 if (ret < 0 && errno != EEXIST) {
130 LOG(ERROR) << "Failed to add loop device";
131 return false;
132 }
133 }
134 LOG(INFO) << "Pre-allocated " << num << " loopback devices";
135 return true;
136 }
137
ConfigureReadAhead(const string & devicePath)138 bool ConfigureReadAhead(const string &devicePath)
139 {
140 if (!StartsWith(devicePath, DEVICE_PREFIX)) {
141 LOG(ERROR) << "invalid device path " << devicePath;
142 return false;
143 }
144 string path(devicePath);
145 string deviceName = basename(&path[0]);
146 string sysfsDevice = SYSTEM_BLOCK_PATH + deviceName + READ_AHEAD_NAME;
147 string realPath = GetRealPath(sysfsDevice);
148 if (realPath.empty()) {
149 LOG(ERROR) << "invalid device path " << sysfsDevice;
150 return false;
151 }
152
153 struct stat fileState;
154 if (stat(realPath.c_str(), &fileState) != 0) {
155 LOG(ERROR) << "Fail to Stat file: " << realPath << ", errno=" << errno;
156 return false;
157 }
158 ON_SCOPE_EXIT(recoveryMode) {
159 (void)chmod(realPath.c_str(), fileState.st_mode);
160 };
161 UniqueFd sysfsFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
162 if (sysfsFd.Get() == -1) {
163 // 0644: give permission to write
164 if (chmod(realPath.c_str(), 0644) != 0) {
165 LOG(WARNING) << "Fail to chmod file: " << realPath << ", errno=" << errno;
166 }
167 sysfsFd = UniqueFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
168 if (sysfsFd.Get() == -1) {
169 LOG(ERROR) << "Fail to open file: " << realPath << ", errno=" << errno;
170 return false;
171 }
172 }
173 int writeBytes = write(sysfsFd.Get(), READ_AHEAD_KB, strlen(READ_AHEAD_KB) + 1);
174 if (writeBytes < 0) {
175 LOG(ERROR) << "Failed to write to " << realPath;
176 return false;
177 }
178 return true;
179 }
180
CheckIfSupportLoopConfigure(const int deviceFd)181 bool CheckIfSupportLoopConfigure(const int deviceFd)
182 {
183 #ifdef LOOP_CONFIGURE
184 struct loop_config config;
185 (void)memset_s(&config, sizeof(config), 0, sizeof(config));
186 config.fd = -1;
187 return ioctl(deviceFd, LOOP_CONFIGURE, &config) == -1 && errno == EBADF;
188 #else
189 return false;
190 #endif
191 }
192
ConfigureLoopDevice(const int deviceFd,const int targetFd,struct loop_info64 li,const bool useBufferedIo)193 bool ConfigureLoopDevice(const int deviceFd, const int targetFd, struct loop_info64 li, const bool useBufferedIo)
194 {
195 #ifdef LOOP_CONFIGURE
196 struct loop_config config;
197 (void)memset_s(&config, sizeof(config), 0, sizeof(config));
198 config.fd = targetFd;
199 config.info = li;
200 config.block_size = LOOP_BLOCK_SIZE;
201 if (!useBufferedIo) {
202 li.lo_flags |= LO_FLAGS_DIRECT_IO;
203 }
204 int ret = ioctl(deviceFd, LOOP_CONFIGURE, &config);
205 if (ret < 0) {
206 LOG(ERROR) << "Failed to configure loop device err=" << errno;
207 return false;
208 }
209 return true;
210 #else
211 return false;
212 #endif
213 }
214
SetLoopDeviceStatus(const int deviceFd,const int targetFd,const struct loop_info64 * li)215 bool SetLoopDeviceStatus(const int deviceFd, const int targetFd, const struct loop_info64 *li)
216 {
217 int ret = ioctl(deviceFd, LOOP_SET_FD, targetFd);
218 if (ret < 0) {
219 LOG(ERROR) << "Failed to set loop fd err=" << errno;
220 return false;
221 }
222 ret = ioctl(deviceFd, LOOP_SET_STATUS64, li);
223 if (ret < 0) {
224 LOG(ERROR) << "Failed to set loop status err=" << errno;
225 return false;
226 }
227 ret = ioctl(deviceFd, BLKFLSBUF, 0);
228 if (ret < 0) {
229 LOG(WARNING) << "Failed to flush buffers on the loop device err=" << errno;
230 }
231 ret = ioctl(deviceFd, LOOP_SET_BLOCK_SIZE, LOOP_BLOCK_SIZE);
232 if (ret < 0) {
233 LOG(WARNING) << "Failed to set block size err=" << errno;
234 }
235 return true;
236 }
237
SetUpLoopDevice(const int deviceFd,const string & target,const uint32_t imageOffset,const uint32_t imageSize)238 bool SetUpLoopDevice(const int deviceFd, const string &target, const uint32_t imageOffset, const uint32_t imageSize)
239 {
240 static bool useLoopConfigure = CheckIfSupportLoopConfigure(deviceFd);
241 bool useBufferedIo = false;
242 string realPath = GetRealPath(target);
243 if (realPath.empty()) {
244 LOG(ERROR) << "invalid target " << target;
245 return false;
246 }
247 UniqueFd targetFd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECT));
248 if (targetFd.Get() == -1) {
249 struct statfs stbuf;
250 int savedErrno = errno;
251 if (statfs(realPath.c_str(), &stbuf) != 0 ||
252 (stbuf.f_type != EROFS_SUPER_MAGIC_V1 &&
253 stbuf.f_type != SQUASHFS_MAGIC &&
254 stbuf.f_type != OVERLAYFS_SUPER_MAGIC &&
255 stbuf.f_type != EXT4_SUPER_MAGIC)) {
256 LOG(ERROR) << "Failed to open " << realPath << " errno=" << savedErrno;
257 return false;
258 }
259 LOG(WARNING) << "Fallback to buffered I/O for " << realPath;
260 useBufferedIo = true;
261 targetFd = UniqueFd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC));
262 if (targetFd.Get() == -1) {
263 LOG(ERROR) << "Failed to open " << realPath;
264 return false;
265 }
266 }
267
268 struct loop_info64 li;
269 (void)memset_s(&li, sizeof(li), 0, sizeof(li));
270 errno_t ret = strcpy_s(reinterpret_cast<char*>(li.lo_crypt_name), LO_NAME_SIZE, MODULE_LOOP_PREFIX);
271 if (ret != EOK) {
272 LOG(ERROR) << "Failed to copy loop prefix " << MODULE_LOOP_PREFIX;
273 return false;
274 }
275 li.lo_offset = imageOffset;
276 li.lo_sizelimit = imageSize;
277 li.lo_flags |= LO_FLAGS_AUTOCLEAR;
278 return useLoopConfigure ? ConfigureLoopDevice(deviceFd, targetFd.Get(), li, useBufferedIo)
279 : SetLoopDeviceStatus(deviceFd, targetFd.Get(), &li);
280 }
281
WaitForDevice(const int num)282 std::unique_ptr<LoopbackDeviceUniqueFd> WaitForDevice(const int num)
283 {
284 const std::vector<string> candidateDevices = {
285 LOOP_BLOCK_PATH + std::to_string(num),
286 LOOP_DEV_PATH + std::to_string(num),
287 };
288
289 UniqueFd sysfsFd;
290 for (size_t i = 0; i < LOOP_DEVICE_RETRY_ATTEMPTS; ++i) {
291 for (const auto &device : candidateDevices) {
292 string realPath = GetRealPath(device);
293 if (realPath.empty()) {
294 continue;
295 }
296 sysfsFd = UniqueFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
297 if (sysfsFd.Get() != -1) {
298 return std::make_unique<LoopbackDeviceUniqueFd>(std::move(sysfsFd), realPath);
299 }
300 }
301 LOG(WARNING) << "Loopback device " << num << " not ready. Waiting 50ms...";
302 usleep(std::chrono::duration_cast<std::chrono::microseconds>(WAIT_FOR_DEVICE_TIME).count());
303 }
304 LOG(ERROR) << "Failed to open loopback device " << num;
305 return nullptr;
306 }
307
CreateLoopDevice(const string & target,const uint32_t imageOffset,const uint32_t imageSize)308 std::unique_ptr<LoopbackDeviceUniqueFd> CreateLoopDevice(
309 const string &target, const uint32_t imageOffset, const uint32_t imageSize)
310 {
311 UniqueFd ctlFd(open(LOOP_CTL_PATH, O_RDWR | O_CLOEXEC));
312 if (ctlFd.Get() == -1) {
313 LOG(ERROR) << "Failed to open loop-control";
314 return nullptr;
315 }
316 static std::mutex mlock;
317 std::lock_guard lock(mlock);
318 int num = ioctl(ctlFd.Get(), LOOP_CTL_GET_FREE);
319 if (num < 0) {
320 LOG(ERROR) << "Failed to get free loop device err=" << errno;
321 return nullptr;
322 }
323 LOG(INFO) << "Get free loop device num " << num;
324 std::unique_ptr<LoopbackDeviceUniqueFd> loopDevice = WaitForDevice(num);
325 if (loopDevice == nullptr) {
326 LOG(ERROR) << "Failed to create loop device " << num;
327 return nullptr;
328 }
329 if (!SetUpLoopDevice(loopDevice->deviceFd.Get(), target, imageOffset, imageSize)) {
330 LOG(ERROR) << "Failed to configure device";
331 return nullptr;
332 }
333 if (!ConfigureReadAhead(loopDevice->name)) {
334 LOG(ERROR) << "Failed to configure read ahead";
335 return nullptr;
336 }
337 return loopDevice;
338 }
339
RemoveDmLoopDevice(const std::string & mountPoint,const std::string & imagePath)340 bool RemoveDmLoopDevice(const std::string &mountPoint, const std::string &imagePath)
341 {
342 struct dirent *ent = nullptr;
343 DIR *dir = nullptr;
344
345 if ((dir = opendir(BLOCK_DEV_PATH)) == nullptr) {
346 LOG(ERROR) << "Failed to open loop dir";
347 return false;
348 }
349 bool ret = false;
350 std::string loopDevPath = "";
351 while ((ent = readdir(dir)) != nullptr) {
352 if (strncmp(ent->d_name, "loop", LOOP_LENGTH) || !strncmp(ent->d_name, "loop-control", LOOP_CTL_LENGTH)) {
353 continue;
354 }
355
356 loopDevPath = std::string(BLOCK_DEV_PATH) + "/" + std::string(ent->d_name);
357 if (!IsRealPath(loopDevPath)) {
358 LOG(ERROR) << "Dev is not exist, loopDevPath=" << loopDevPath;
359 loopDevPath = "";
360 continue;
361 }
362 if (!IsLoopDevMatchedImg(loopDevPath, imagePath)) {
363 loopDevPath = "";
364 continue;
365 }
366 if (umount(mountPoint.c_str()) != 0) {
367 LOG(WARNING) << "Could not umount " << mountPoint << " errno: " << errno;
368 }
369 bool clearDm = (imagePath.find(UPDATE_ACTIVE_DIR) != std::string::npos);
370 ret = ClearDmLoopDevice(loopDevPath, clearDm);
371 break;
372 }
373 closedir(dir);
374 return ret;
375 }
376
RemoveDmLoopDevice(const std::string & loopDevPath)377 bool RemoveDmLoopDevice(const std::string &loopDevPath)
378 {
379 #ifndef USER_DEBUG_MODE
380 if (!RemoveDmDevice(loopDevPath)) {
381 LOG(ERROR) << "Close dm error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
382 return false;
383 }
384 #endif
385 if (!CloseLoopDev(loopDevPath)) {
386 LOG(ERROR) << "Close loop error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
387 return false;
388 }
389 return true;
390 }
391
ClearDmLoopDevice(const std::string & loopDevPath,const bool clearDm)392 bool ClearDmLoopDevice(const std::string &loopDevPath, const bool clearDm)
393 {
394 #ifndef USER_DEBUG_MODE
395 if (clearDm) {
396 if (!RemoveDmDevice(loopDevPath)) {
397 LOG(ERROR) << "Close dm error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
398 return false;
399 }
400 }
401 #endif
402 if (!CloseLoopDev(loopDevPath)) {
403 LOG(ERROR) << "Close loop error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
404 return false;
405 }
406 return true;
407 }
408
IsLoopDevMatchedImg(const std::string & loopPath,const std::string & imgFilePath)409 bool IsLoopDevMatchedImg(const std::string &loopPath, const std::string &imgFilePath)
410 {
411 struct loop_info64 info;
412 if (memset_s(&info, sizeof(struct loop_info64), 0, sizeof(struct loop_info64)) != EOK) {
413 LOG(ERROR) << "memset_s failed";
414 return false;
415 }
416
417 int fd = open(loopPath.c_str(), O_RDWR | O_CLOEXEC);
418 if (fd == -1) {
419 LOG(ERROR) << "Open failed, loopPath=" << loopPath.c_str() << ", errno=" << errno;
420 return false;
421 }
422
423 if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
424 close(fd);
425 return false;
426 }
427 close(fd);
428 return (imgFilePath == std::string(reinterpret_cast<char *>(info.lo_file_name)));
429 }
430
CloseLoopDev(const std::string & loopPath)431 bool CloseLoopDev(const std::string &loopPath)
432 {
433 struct stat st;
434 if (stat(loopPath.c_str(), &st)) {
435 LOG(INFO) << "Stat error, loopPath=" << loopPath.c_str() << ", errno=" << errno;
436 return false;
437 }
438
439 int userFd = open(loopPath.c_str(), O_RDWR);
440 if (userFd < 0) {
441 LOG(ERROR) << "Open error, loopPath=" << loopPath.c_str() << ", errno=" << errno;
442 return false;
443 }
444
445 int ret = ioctl(userFd, LOOP_CLR_FD);
446 close(userFd);
447 if (ret != 0) {
448 LOG(ERROR) << "Clear error, loopPath=" << loopPath.c_str() << ", errno=" << errno;
449 return false;
450 }
451 return true;
452 }
453 } // Loop
454 } // SysInstaller
455 } // namespace OHOS