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 "init_eng.h"
17
18#include <dirent.h>
19#include <limits.h>
20#include <sys/mount.h>
21
22#include "plugin_adapter.h"
23#include "init_cmds.h"
24#include "init_utils.h"
25#include "init_module_engine.h"
26#include "securec.h"
27
28#define ENG_SYSTEM_DEVICE_PATH "/dev/block/by-name/eng_system"
29#define ENG_CHIPSET_DEVICE_PATH "/dev/block/by-name/eng_chipset"
30
31ENG_STATIC bool IsFileExistWithType(const char *file, FileType type)
32{
33    bool isExist = false;
34    struct stat st = {};
35    if (lstat(file, &st) == 0) {
36        switch (type) {
37            case TYPE_DIR:
38                if (S_ISDIR(st.st_mode)) {
39                    isExist = true;
40                }
41                break;
42            case TYPE_REG:
43                if (S_ISREG(st.st_mode)) {
44                    isExist = true;
45                }
46                break;
47            case TYPE_LINK:
48                if (S_ISLNK(st.st_mode)) {
49                    isExist = true;
50                }
51                break;
52            case TYPE_ANY: // fallthrough
53            default:
54                isExist = true;
55                break;
56        }
57    }
58    return isExist;
59}
60
61static bool IsRegularFile(const char *file)
62{
63    return file == NULL ? false : IsFileExistWithType(file, TYPE_REG);
64}
65
66static bool IsExistFile(const char *file)
67{
68    return file == NULL ? false : IsFileExistWithType(file, TYPE_ANY);
69}
70
71ENG_STATIC void BuildMountCmd(char *buffer, size_t len, const char *mp, const char *dev, const char *fstype)
72{
73    int ret = snprintf_s(buffer, len, len - 1, "%s %s %s ro barrier=1",
74        fstype, dev, mp);
75    if (ret == -1) {
76        *buffer = '\0';
77    }
78}
79
80ENG_STATIC void MountEngPartitions(void)
81{
82    char mountCmd[MOUNT_CMD_MAX_LEN] = {};
83   // Mount eng_system
84    BuildMountCmd(mountCmd, MOUNT_CMD_MAX_LEN, "/eng_system",
85        "/dev/block/by-name/eng_system", "ext4");
86    WaitForFile(ENG_SYSTEM_DEVICE_PATH, WAIT_MAX_SECOND);
87    int cmdIndex = 0;
88    (void)GetMatchCmd("mount ", &cmdIndex);
89    DoCmdByIndex(cmdIndex, mountCmd, NULL);
90
91   // Mount eng_chipset
92    BuildMountCmd(mountCmd, MOUNT_CMD_MAX_LEN, "/eng_chipset",
93        "/dev/block/by-name/eng_chipset", "ext4");
94    WaitForFile(ENG_CHIPSET_DEVICE_PATH, WAIT_MAX_SECOND);
95    DoCmdByIndex(cmdIndex, mountCmd, NULL);
96}
97
98ENG_STATIC void BindMountFile(const char *source, const char *target)
99{
100    char targetFullPath[PATH_MAX] = {};
101    const char *p = source;
102    char *q = NULL;
103    const char *end = source + strlen(source);
104
105    if (*p != '/') { // source must start with '/'
106        return;
107    }
108
109    // Get next '/'
110    q = strchr(p + 1, '/');
111    if (q == NULL) {
112        PLUGIN_LOGI("path \' %s \' without extra slash, ignore it", source);
113        return;
114    }
115
116    if (*(end - 1) == '/') {
117        PLUGIN_LOGI("path \' %s \' ends with slash, ignore it", source);
118        return;
119    }
120    // OK, now get sub dir and combine it with target
121    int ret = snprintf_s(targetFullPath, PATH_MAX, PATH_MAX - 1, "%s%s", strcmp(target, "/") == 0 ? "" : target, q);
122    if (ret == -1) {
123        PLUGIN_LOGE("Failed to build target path");
124        return;
125    }
126    PLUGIN_LOGI("target full path is %s", targetFullPath);
127    if (IsRegularFile(targetFullPath)) {
128        if (mount(source, targetFullPath, NULL, MS_BIND, NULL) != 0) {
129            PLUGIN_LOGE("Failed to bind mount %s to %s, err = %d", source, targetFullPath, errno);
130        } else {
131            PLUGIN_LOGI("Bind mount %s to %s done", source, targetFullPath);
132        }
133    } else {
134        if (!IsExistFile(targetFullPath)) {
135            if (symlink(source, targetFullPath) < 0) {
136                PLUGIN_LOGE("Failed to link %s to %s, err = %d", source, targetFullPath, errno);
137            }
138        } else {
139            PLUGIN_LOGW("%s without expected type, skip overlay", targetFullPath);
140        }
141    }
142}
143
144ENG_STATIC void DebugFilesOverlay(const char *source, const char *target)
145{
146    DIR *dir = NULL;
147    struct dirent *de = NULL;
148
149    if ((dir = opendir(source)) == NULL) {
150        PLUGIN_LOGE("Open path \' %s \' failed. err = %d", source, errno);
151        return;
152    }
153    int dfd = dirfd(dir);
154    char srcPath[PATH_MAX] = {};
155    while ((de = readdir(dir)) != NULL) {
156        if (de->d_name[0] == '.') {
157            continue;
158        }
159        if (snprintf_s(srcPath, PATH_MAX, PATH_MAX - 1, "%s/%s", source, de->d_name) == -1) {
160            PLUGIN_LOGE("Failed to build path for overlaying");
161            break;
162        }
163
164        // Determine file type
165        struct stat st = {};
166        if (fstatat(dfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
167            continue;
168        }
169        if (S_ISDIR(st.st_mode)) {
170            DebugFilesOverlay(srcPath, target);
171        } else if (S_ISREG(st.st_mode)) {
172            BindMountFile(srcPath, target);
173        } else { // Ignore any other file types
174            PLUGIN_LOGI("Ignore %s while overlaying", srcPath);
175        }
176    }
177    closedir(dir);
178    dir = NULL;
179}
180
181ENG_STATIC void EngineerOverlay(void)
182{
183    PLUGIN_LOGI("system overlay...");
184    DebugFilesOverlay("/eng_system", "/");
185    PLUGIN_LOGI("vendor overlay...");
186    DebugFilesOverlay("/eng_chipset", "/chipset");
187}
188
189MODULE_CONSTRUCTOR(void)
190{
191    PLUGIN_LOGI("Start eng mode now ...");
192    MountEngPartitions();
193    EngineerOverlay();
194}
195