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