xref: /base/startup/init/remount/remount_overlay.c (revision d9f0492f)
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}