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 41namespace OHOS { 42namespace SysInstaller { 43namespace Loop { 44using namespace Updater; 45using std::string; 46 47namespace { 48const int LOOP_LENGTH = 4; 49const int LOOP_CTL_LENGTH = 12; 50constexpr const char *LOOP_CTL_PATH = "/dev/loop-control"; 51constexpr const char *BLOCK_DEV_PATH = "/dev/block"; 52constexpr const char *LOOP_PREFIX = "loop"; 53constexpr const char *DEVICE_PREFIX = "/dev/"; 54constexpr const char *SYSTEM_BLOCK_PATH = "/sys/block/"; 55constexpr const char *READ_AHEAD_NAME = "/queue/read_ahead_kb"; 56constexpr const char *READ_AHEAD_KB = "128"; 57constexpr const char *MODULE_LOOP_PREFIX = "module:"; 58constexpr const char *LOOP_DEV_PATH = "/dev/loop"; 59constexpr const char *LOOP_BLOCK_PATH = "/dev/block/loop"; 60const size_t LOOP_DEVICE_RETRY_ATTEMPTS = 6u; 61const uint32_t LOOP_BLOCK_SIZE = 4096; 62const std::chrono::milliseconds WAIT_FOR_DEVICE_TIME(50); 63const std::chrono::seconds WAIT_FOR_LOOP_TIME(50); 64} 65 66static 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 76void 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 86bool 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 138bool 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 181bool 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 193bool 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 215bool 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 238bool 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 282std::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 308std::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 340bool 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 377bool 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 392bool 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 409bool 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 431bool 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