/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "init_eng.h" #include #include #include #include "plugin_adapter.h" #include "init_cmds.h" #include "init_utils.h" #include "init_module_engine.h" #include "fs_manager/fs_manager.h" #include "securec.h" #define ENG_SYSTEM_DEVICE_PATH "/dev/block/by-name/eng_system" #define ENG_CHIPSET_DEVICE_PATH "/dev/block/by-name/eng_chipset" #define DEV_NAME_MAX_LEN 64 ENG_STATIC bool IsFileExistWithType(const char *file, FileType type) { bool isExist = false; struct stat st = {}; if (lstat(file, &st) == 0) { switch (type) { case TYPE_DIR: if (S_ISDIR(st.st_mode)) { isExist = true; } break; case TYPE_REG: if (S_ISREG(st.st_mode)) { isExist = true; } break; case TYPE_LINK: if (S_ISLNK(st.st_mode)) { isExist = true; } break; case TYPE_ANY: // fallthrough default: isExist = true; break; } } return isExist; } static bool IsRegularFile(const char *file) { return file == NULL ? false : IsFileExistWithType(file, TYPE_REG); } static bool IsExistFile(const char *file) { return file == NULL ? false : IsFileExistWithType(file, TYPE_ANY); } ENG_STATIC void BuildMountCmd(char *buffer, size_t len, const char *mp, const char *dev, const char *fstype) { int ret = snprintf_s(buffer, len, len - 1, "%s %s %s ro barrier=1", fstype, dev, mp); if (ret == -1) { *buffer = '\0'; } } ENG_STATIC void MountOneEngPartition(const char *partition, const char *devName) { char mountCmd[MOUNT_CMD_MAX_LEN] = {}; BuildMountCmd(mountCmd, MOUNT_CMD_MAX_LEN, partition, devName, "ext4"); WaitForFile(devName, WAIT_MAX_SECOND); int cmdIndex = 0; (void)GetMatchCmd("mount ", &cmdIndex); DoCmdByIndex(cmdIndex, mountCmd, NULL); } ENG_STATIC void MountEngPartitions(void) { if (GetBootSlots() <= 1) { MountOneEngPartition("/eng_system", "/dev/block/by-name/eng_system"); MountOneEngPartition("/eng_chipset", "/dev/block/by-name/eng_chipset"); return; } // mount AB partition int currentSlot = GetCurrentSlot(); if (currentSlot > 0 && currentSlot <= MAX_SLOT) { char slotChar = 'a' + currentSlot - 1; char systemDevName[DEV_NAME_MAX_LEN] = {0}; int ret = snprintf_s(systemDevName, DEV_NAME_MAX_LEN, DEV_NAME_MAX_LEN - 1, "%s_%c", "/dev/block/by-name/eng_system", slotChar); if (ret <= 0) { PLUGIN_LOGE("snprintf_s systemDevName failed"); return; } if (access(systemDevName, F_OK) != 0) { MountOneEngPartition("/eng_system", "/dev/block/by-name/eng_system"); } else { MountOneEngPartition("/eng_system", systemDevName); } char chipsetDevName[DEV_NAME_MAX_LEN] = {0}; ret = snprintf_s(chipsetDevName, DEV_NAME_MAX_LEN, DEV_NAME_MAX_LEN - 1, "%s_%c", "/dev/block/by-name/eng_chipset", slotChar); if (ret <= 0) { PLUGIN_LOGE("snprintf_s chipsetDevName failed"); return; } if (access(chipsetDevName, F_OK) != 0) { MountOneEngPartition("/eng_chipset", "/dev/block/by-name/eng_chipset"); } else { MountOneEngPartition("/eng_chipset", chipsetDevName); } return; } MountOneEngPartition("/eng_system", "/dev/block/by-name/eng_system"); MountOneEngPartition("/eng_chipset", "/dev/block/by-name/eng_chipset"); } ENG_STATIC void BindMountFile(const char *source, const char *target) { char targetFullPath[PATH_MAX] = {}; const char *p = source; char *q = NULL; const char *end = source + strlen(source); if (*p != '/') { // source must start with '/' return; } // Get next '/' q = strchr(p + 1, '/'); if (q == NULL) { PLUGIN_LOGI("path \' %s \' without extra slash, ignore it", source); return; } if (*(end - 1) == '/') { PLUGIN_LOGI("path \' %s \' ends with slash, ignore it", source); return; } // OK, now get sub dir and combine it with target int ret = snprintf_s(targetFullPath, PATH_MAX, PATH_MAX - 1, "%s%s", strcmp(target, "/") == 0 ? "" : target, q); if (ret == -1) { PLUGIN_LOGE("Failed to build target path"); return; } PLUGIN_LOGI("target full path is %s", targetFullPath); if (IsRegularFile(targetFullPath)) { if (mount(source, targetFullPath, NULL, MS_BIND, NULL) != 0) { PLUGIN_LOGE("Failed to bind mount %s to %s, err = %d", source, targetFullPath, errno); } else { PLUGIN_LOGI("Bind mount %s to %s done", source, targetFullPath); } } else { if (!IsExistFile(targetFullPath)) { if (symlink(source, targetFullPath) < 0) { PLUGIN_LOGE("Failed to link %s to %s, err = %d", source, targetFullPath, errno); } } else { PLUGIN_LOGW("%s without expected type, skip overlay", targetFullPath); } } } ENG_STATIC void DebugFilesOverlay(const char *source, const char *target) { DIR *dir = NULL; struct dirent *de = NULL; if ((dir = opendir(source)) == NULL) { PLUGIN_LOGE("Open path \' %s \' failed. err = %d", source, errno); return; } int dfd = dirfd(dir); char srcPath[PATH_MAX] = {}; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] == '.') { continue; } if (snprintf_s(srcPath, PATH_MAX, PATH_MAX - 1, "%s/%s", source, de->d_name) == -1) { PLUGIN_LOGE("Failed to build path for overlaying"); break; } // Determine file type struct stat st = {}; if (fstatat(dfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { continue; } if (S_ISDIR(st.st_mode)) { DebugFilesOverlay(srcPath, target); } else if (S_ISREG(st.st_mode)) { BindMountFile(srcPath, target); } else { // Ignore any other file types PLUGIN_LOGI("Ignore %s while overlaying", srcPath); } } closedir(dir); dir = NULL; } ENG_STATIC void EngineerOverlay(void) { PLUGIN_LOGI("system overlay..."); DebugFilesOverlay("/eng_system", "/"); PLUGIN_LOGI("vendor overlay..."); DebugFilesOverlay("/eng_chipset", "/chipset"); } MODULE_CONSTRUCTOR(void) { PLUGIN_LOGI("Start eng mode now ..."); MountEngPartitions(); EngineerOverlay(); }