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 INIT_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 
IsSkipRemount(const struct mntent mentry)51 INIT_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 
ExecCommand(int argc, char **argv)69 INIT_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 
GetDevSize(const char *fsBlkDev, uint64_t *devSize)90 INIT_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 
FormatExt4(const char *fsBlkDev, const char *fsMntPoint)109 INIT_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 
OverlayRemountPre(const char *mnt)146 INIT_STATIC void OverlayRemountPre(const char *mnt)
147 {
148     if (strcmp(mnt, MNT_VENDOR) == 0) {
149         OverlayRemountVendorPre();
150     }
151 }
152 
OverlayRemountPost(const char *mnt)153 INIT_STATIC void OverlayRemountPost(const char *mnt)
154 {
155     if (strcmp(mnt, MNT_VENDOR) == 0) {
156         OverlayRemountVendorPost();
157     }
158 }
159 
DoRemount(struct mntent *mentry, bool *result)160 INIT_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 
DirectoryExists(const char *path)214 INIT_STATIC bool DirectoryExists(const char *path)
215 {
216     struct stat sb;
217     return stat(path, &sb) != -1 && S_ISDIR(sb.st_mode);
218 }
219 
RootOverlaySetup(void)220 int 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 
DoSystemRemount(struct mntent *mentry, bool *result)258 INIT_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 
IsRegularFile(const char *file)299 static 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 
MountBindEngFile(const char *source, const char *target)310 static 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 
EngFilesOverlay(const char *source, const char *target)356 static 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 
RemountRofsOverlaynull393 bool 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 }