1/* 2 * Copyright (c) 2024-2024 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 <sys/ioctl.h> 17#include <sys/stat.h> 18#include <sys/mount.h> 19#include <sys/wait.h> 20#include <mntent.h> 21#include <dirent.h> 22#include "securec.h" 23#include "init_log.h" 24#include "init_utils.h" 25#include "fs_manager/fs_manager.h" 26#include "erofs_mount_overlay.h" 27#include "erofs_remount_overlay.h" 28#include "remount_overlay.h" 29 30#define MODE_MKDIR 0755 31#define BLOCK_SIZE_UNIT 4096 32#define PATH_MAX 256 33#define PREFIX_LOWER "/mnt/lower" 34#define MNT_VENDOR "/vendor" 35#define ROOT_MOUNT_DIR "/" 36#define SYSTEM_DIR "/usr" 37 38INIT_STATIC bool MntNeedRemount(const char *mnt) 39{ 40 char *remountPath[] = { 41 "/", "/vendor", "/sys_prod", "/chip_prod", "/preload", "/cust", "/version", "/patch_hw" 42 }; 43 for (size_t i = 0; i < ARRAY_LENGTH(remountPath); i++) { 44 if (strcmp(remountPath[i], mnt) == 0) { 45 return true; 46 } 47 } 48 return false; 49} 50 51INIT_STATIC bool IsSkipRemount(const struct mntent mentry) 52{ 53 if (mentry.mnt_type == NULL || mentry.mnt_dir == NULL) { 54 return true; 55 } 56 if (!MntNeedRemount(mentry.mnt_dir)) { 57 return true; 58 } 59 if (strncmp(mentry.mnt_type, "erofs", strlen("erofs")) != 0) { 60 return true; 61 } 62 63 if (strncmp(mentry.mnt_fsname, "/dev/block/dm-", strlen("/dev/block/dm-")) != 0) { 64 return true; 65 } 66 return false; 67} 68 69INIT_STATIC int ExecCommand(int argc, char **argv) 70{ 71 INIT_CHECK(!(argc == 0 || argv == NULL || argv[0] == NULL), return -1); 72 73 INIT_LOGI("Execute %s begin", argv[0]); 74 pid_t pid = fork(); 75 INIT_ERROR_CHECK(pid >= 0, return -1, "Fork new process to format failed: %d", errno); 76 77 if (pid == 0) { 78 execv(argv[0], argv); 79 exit(-1); 80 } 81 int status; 82 waitpid(pid, &status, 0); 83 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 84 INIT_LOGE("Command %s failed with status %d", argv[0], WEXITSTATUS(status)); 85 } 86 INIT_LOGI("Execute %s end", argv[0]); 87 return WEXITSTATUS(status); 88} 89 90INIT_STATIC int GetDevSize(const char *fsBlkDev, uint64_t *devSize) 91{ 92 int fd = -1; 93 fd = open(fsBlkDev, O_RDONLY); 94 if (fd < 0) { 95 INIT_LOGE("open %s failed errno %d", fsBlkDev, errno); 96 return -1; 97 } 98 99 if (ioctl(fd, BLKGETSIZE64, devSize) < 0) { 100 INIT_LOGE("get block device [%s] size failed, errno %d", fsBlkDev, errno); 101 close(fd); 102 return -1; 103 } 104 105 close(fd); 106 return 0; 107} 108 109INIT_STATIC int FormatExt4(const char *fsBlkDev, const char *fsMntPoint) 110{ 111 uint64_t devSize; 112 int ret = GetDevSize(fsBlkDev, &devSize); 113 if (ret) { 114 INIT_LOGE("get dev size failed."); 115 return ret; 116 } 117 118 char blockSizeBuffer[MAX_BUFFER_LEN] = {0}; 119 if (snprintf_s(blockSizeBuffer, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, "%llu", devSize / BLOCK_SIZE_UNIT) < 0) { 120 BEGET_LOGE("Failed to copy nameRofs."); 121 return -1; 122 } 123 124 const char *mke2fsArgs[] = { 125 "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fsBlkDev, blockSizeBuffer, NULL 126 }; 127 int mke2fsArgsLen = ARRAY_LENGTH(mke2fsArgs); 128 ret = ExecCommand(mke2fsArgsLen, (char **)mke2fsArgs); 129 if (ret) { 130 INIT_LOGE("mke2fs failed returned %d", ret); 131 return -1; 132 } 133 134 const char *e2fsdroidArgs[] = { 135 "system/bin/e2fsdroid", "-e", "-a", fsMntPoint, fsBlkDev, NULL 136 137 }; 138 int e2fsdroidArgsLen = ARRAY_LENGTH(e2fsdroidArgs); 139 ret = ExecCommand(e2fsdroidArgsLen, (char **)e2fsdroidArgs); 140 if (ret) { 141 INIT_LOGE("e2fsdroid failed returned %d", ret); 142 } 143 return 0; 144} 145 146INIT_STATIC void OverlayRemountPre(const char *mnt) 147{ 148 if (strcmp(mnt, MNT_VENDOR) == 0) { 149 OverlayRemountVendorPre(); 150 } 151} 152 153INIT_STATIC void OverlayRemountPost(const char *mnt) 154{ 155 if (strcmp(mnt, MNT_VENDOR) == 0) { 156 OverlayRemountVendorPost(); 157 } 158} 159 160INIT_STATIC bool DoRemount(struct mntent *mentry, bool *result) 161{ 162 int devNum = 0; 163 char *mnt = NULL; 164 int ret = 0; 165 ret = sscanf_s(mentry->mnt_fsname + strlen("/dev/block/dm-"), "%d", &devNum); 166 if (ret < 0) { 167 INIT_LOGE("get devNum failed returned"); 168 return false; 169 } 170 171 char devExt4[MAX_BUFFER_LEN] = {0}; 172 devNum = devNum + 1; 173 if (snprintf_s(devExt4, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, "/dev/block/dm-%d", devNum) < 0) { 174 INIT_LOGE("Failed to copy devExt4."); 175 return false; 176 } 177 178 if (strncmp(mentry->mnt_dir, PREFIX_LOWER, strlen(PREFIX_LOWER)) == 0) { 179 mnt = mentry->mnt_dir + strlen(PREFIX_LOWER); 180 } else { 181 mnt = mentry->mnt_dir; 182 } 183 184 if (CheckIsExt4(devExt4, 0)) { 185 INIT_LOGI("is ext4, not need format %s", devExt4); 186 } else { 187 ret = FormatExt4(devExt4, mnt); 188 if (ret) { 189 INIT_LOGE("Failed to format devExt4 %s.", devExt4); 190 return false; 191 } 192 193 ret = MountExt4Device(devExt4, mnt, true); 194 if (ret) { 195 INIT_LOGE("Failed to mount devExt4 %s.", devExt4); 196 return false; 197 } 198 } 199 200 if (strncmp(mentry->mnt_dir, "/mnt/lower", strlen("/mnt/lower")) == 0) { 201 return true; 202 } 203 204 OverlayRemountPre(mnt); 205 if (MountOverlayOne(mnt)) { 206 INIT_LOGE("Failed to mount overlay on mnt:%s.", mnt); 207 return false; 208 } 209 OverlayRemountPost(mnt); 210 *result = true; 211 return true; 212} 213 214INIT_STATIC bool DirectoryExists(const char *path) 215{ 216 struct stat sb; 217 return stat(path, &sb) != -1 && S_ISDIR(sb.st_mode); 218} 219 220int RootOverlaySetup(void) 221{ 222 const char *rootOverlay = "/mnt/overlay/usr"; 223 const char *rootUpper = "/mnt/overlay/usr/upper"; 224 const char *rootWork = "/mnt/overlay/usr/work"; 225 char mntOpt[MAX_BUFFER_LEN] = {0}; 226 227 if (snprintf_s(mntOpt, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, 228 "upperdir=%s,lowerdir=/system,workdir=%s,override_creds=off", rootUpper, rootWork) < 0) { 229 INIT_LOGE("copy mntOpt failed. errno %d", errno); 230 return -1; 231 } 232 233 if (!DirectoryExists(rootOverlay)) { 234 if (mkdir(rootOverlay, MODE_MKDIR) && (errno != EEXIST)) { 235 INIT_LOGE("make dir failed on %s", rootOverlay); 236 return -1; 237 } 238 239 if (mkdir(rootUpper, MODE_MKDIR) && (errno != EEXIST)) { 240 INIT_LOGE("make dir failed on %s", rootUpper); 241 return -1; 242 } 243 244 if (mkdir(rootWork, MODE_MKDIR) && (errno != EEXIST)) { 245 INIT_LOGE("make dir failed on %s", rootWork); 246 return -1; 247 } 248 } 249 250 if (mount("overlay", "/system", "overlay", 0, mntOpt)) { 251 INIT_LOGE("system mount overlay failed on %s", mntOpt); 252 return -1; 253 } 254 INIT_LOGI("system mount overlay sucess"); 255 return 0; 256} 257 258INIT_STATIC bool DoSystemRemount(struct mntent *mentry, bool *result) 259{ 260 int devNum = 0; 261 int ret = 0; 262 ret = sscanf_s(mentry->mnt_fsname + strlen("/dev/block/dm-"), "%d", &devNum); 263 if (ret < 0) { 264 INIT_LOGE("get devNum failed returned"); 265 return false; 266 } 267 268 char devExt4[MAX_BUFFER_LEN] = {0}; 269 devNum = devNum + 1; 270 if (snprintf_s(devExt4, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, "/dev/block/dm-%d", devNum) < 0) { 271 BEGET_LOGE("Failed to copy devExt4."); 272 return false; 273 } 274 275 if (CheckIsExt4(devExt4, 0)) { 276 INIT_LOGI("is ext4, not need format %s", devExt4); 277 } else { 278 ret = FormatExt4(devExt4, SYSTEM_DIR); 279 if (ret) { 280 INIT_LOGE("Failed to format devExt4 %s.", devExt4); 281 return false; 282 } 283 284 ret = MountExt4Device(devExt4, SYSTEM_DIR, true); 285 if (ret) { 286 INIT_LOGE("Failed to mount devExt4 %s.", devExt4); 287 } 288 } 289 290 if (RootOverlaySetup()) { 291 INIT_LOGE("Failed to root overlay."); 292 return false; 293 } 294 295 *result = true; 296 return true; 297} 298 299static bool IsRegularFile(const char *file) 300{ 301 struct stat st = {0}; 302 if (lstat(file, &st) == 0) { 303 if (S_ISREG(st.st_mode)) { 304 return true; 305 } 306 } 307 return false; 308} 309 310static void MountBindEngFile(const char *source, const char *target) 311{ 312 char targetFullPath[PATH_MAX] = {0}; 313 const char *p = source; 314 char *q = NULL; 315 const char *end = source + strlen(source); 316 317 if (*p != '/') { // source must start with '/' 318 return; 319 } 320 321 // Get next '/' 322 q = strchr(p + 1, '/'); 323 if (q == NULL) { 324 INIT_LOGI("path \' %s \' without extra slash, ignore it", source); 325 return; 326 } 327 328 if (*(end - 1) == '/') { 329 INIT_LOGI("path \' %s \' ends with slash, ignore it", source); 330 return; 331 } 332 // OK, now get sub dir and combine it with target 333 int ret = snprintf_s(targetFullPath, PATH_MAX, PATH_MAX - 1, "%s%s", strcmp(target, "/") == 0 ? "" : target, q); 334 if (ret == -1) { 335 INIT_LOGE("Failed to build target path"); 336 return; 337 } 338 INIT_LOGI("target full path is %s", targetFullPath); 339 if (access(targetFullPath, F_OK) != 0) { // file not exist, symlink targetFullPath 340 if (symlink(source, targetFullPath) < 0) { 341 INIT_LOGE("Failed to link %s to %s, err = %d", source, targetFullPath, errno); 342 } 343 return; 344 } 345 if (IsRegularFile(targetFullPath)) { // file exist, moung bind targetFullPath 346 if (mount(source, targetFullPath, NULL, MS_BIND, NULL) != 0) { 347 INIT_LOGE("Failed to bind mount %s to %s, err = %d", source, targetFullPath, errno); 348 } else { 349 INIT_LOGI("Bind mount %s to %s done", source, targetFullPath); 350 } 351 return; 352 } 353 INIT_LOGW("%s without expected type, skip overlay", targetFullPath); 354} 355 356static void EngFilesOverlay(const char *source, const char *target) 357{ 358 DIR *dir = NULL; 359 struct dirent *de = NULL; 360 361 if ((dir = opendir(source)) == NULL) { 362 INIT_LOGE("Open path \' %s \' failed. err = %d", source, errno); 363 return; 364 } 365 int dfd = dirfd(dir); 366 char srcPath[PATH_MAX] = {}; 367 while ((de = readdir(dir)) != NULL) { 368 if (de->d_name[0] == '.') { 369 continue; 370 } 371 if (snprintf_s(srcPath, PATH_MAX, PATH_MAX - 1, "%s/%s", source, de->d_name) == -1) { 372 INIT_LOGE("Failed to build path for overlaying"); 373 break; 374 } 375 376 // Determine file type 377 struct stat st = {}; 378 if (fstatat(dfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { 379 continue; 380 } 381 if (S_ISDIR(st.st_mode)) { 382 EngFilesOverlay(srcPath, target); 383 } else if (S_ISREG(st.st_mode)) { 384 MountBindEngFile(srcPath, target); 385 } else { // Ignore any other file types 386 INIT_LOGI("Ignore %s while overlaying", srcPath); 387 } 388 } 389 closedir(dir); 390 dir = NULL; 391} 392 393bool RemountRofsOverlay() 394{ 395 bool result = false; 396 int lastRemountResult = GetRemountResult(); 397 INIT_LOGI("get last remount result is %d.", lastRemountResult); 398 if (lastRemountResult != REMOUNT_NONE) { 399 return (lastRemountResult == REMOUNT_SUCC) ? true : false; 400 } 401 FILE *fp; 402 struct mntent *mentry = NULL; 403 if ((fp = setmntent("/proc/mounts", "r")) == NULL) { 404 INIT_LOGE("Failed to open /proc/mounts."); 405 return false; 406 } 407 408 while (NULL != (mentry = getmntent(fp))) { 409 if (IsSkipRemount(*mentry)) { 410 INIT_LOGI("skip remount %s", mentry->mnt_dir); 411 continue; 412 } 413 414 if (strcmp(mentry->mnt_dir, ROOT_MOUNT_DIR) == 0) { 415 DoSystemRemount(mentry, &result); 416 continue; 417 } 418 INIT_LOGI("do remount %s", mentry->mnt_dir); 419 if (!DoRemount(mentry, &result)) { 420 endmntent(fp); 421 INIT_LOGE("do remount failed on %s", mentry->mnt_dir); 422 return false; 423 } 424 } 425 426 endmntent(fp); 427 SetRemountResultFlag(result); 428 429 INIT_LOGI("remount system overlay..."); 430 EngFilesOverlay("/eng_system", "/"); 431 INIT_LOGI("remount chipset overlay..."); 432 EngFilesOverlay("/eng_chipset", "/chipset"); 433 return true; 434}