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 }