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"
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", "-E", "nodiscard", 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)160 INIT_STATIC bool DoRemount(struct mntent *mentry)
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 return true;
211 }
212
DirectoryExists(const char * path)213 INIT_STATIC bool DirectoryExists(const char *path)
214 {
215 struct stat sb;
216 return stat(path, &sb) != -1 && S_ISDIR(sb.st_mode);
217 }
218
RootOverlaySetup(void)219 int RootOverlaySetup(void)
220 {
221 const char *rootOverlay = "/mnt/overlay/usr";
222 const char *rootUpper = "/mnt/overlay/usr/upper";
223 const char *rootWork = "/mnt/overlay/usr/work";
224 char mntOpt[MAX_BUFFER_LEN] = {0};
225
226 if (snprintf_s(mntOpt, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1,
227 "upperdir=%s,lowerdir=/system,workdir=%s,override_creds=off", rootUpper, rootWork) < 0) {
228 INIT_LOGE("copy mntOpt failed. errno %d", errno);
229 return -1;
230 }
231
232 if (!DirectoryExists(rootOverlay)) {
233 if (mkdir(rootOverlay, MODE_MKDIR) && (errno != EEXIST)) {
234 INIT_LOGE("make dir failed on %s", rootOverlay);
235 return -1;
236 }
237
238 if (mkdir(rootUpper, MODE_MKDIR) && (errno != EEXIST)) {
239 INIT_LOGE("make dir failed on %s", rootUpper);
240 return -1;
241 }
242
243 if (mkdir(rootWork, MODE_MKDIR) && (errno != EEXIST)) {
244 INIT_LOGE("make dir failed on %s", rootWork);
245 return -1;
246 }
247 }
248
249 if (mount("overlay", "/system", "overlay", 0, mntOpt)) {
250 INIT_LOGE("system mount overlay failed on %s", mntOpt);
251 return -1;
252 }
253 INIT_LOGI("system mount overlay sucess");
254 return 0;
255 }
256
DoSystemRemount(struct mntent * mentry)257 INIT_STATIC bool DoSystemRemount(struct mntent *mentry)
258 {
259 int devNum = 0;
260 int ret = 0;
261 ret = sscanf_s(mentry->mnt_fsname + strlen("/dev/block/dm-"), "%d", &devNum);
262 if (ret < 0) {
263 INIT_LOGE("get devNum failed returned");
264 return false;
265 }
266
267 char devExt4[MAX_BUFFER_LEN] = {0};
268 devNum = devNum + 1;
269 if (snprintf_s(devExt4, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, "/dev/block/dm-%d", devNum) < 0) {
270 BEGET_LOGE("Failed to copy devExt4.");
271 return false;
272 }
273
274 if (CheckIsExt4(devExt4, 0)) {
275 INIT_LOGI("is ext4, not need format %s", devExt4);
276 } else {
277 ret = FormatExt4(devExt4, SYSTEM_DIR);
278 if (ret) {
279 INIT_LOGE("Failed to format devExt4 %s.", devExt4);
280 return false;
281 }
282
283 ret = MountExt4Device(devExt4, SYSTEM_DIR, true);
284 if (ret) {
285 INIT_LOGE("Failed to mount devExt4 %s.", devExt4);
286 }
287 }
288
289 if (RootOverlaySetup()) {
290 INIT_LOGE("Failed to root overlay.");
291 return false;
292 }
293
294 return true;
295 }
296
IsRegularFile(const char * file)297 static bool IsRegularFile(const char *file)
298 {
299 struct stat st = {0};
300 if (lstat(file, &st) == 0) {
301 if (S_ISREG(st.st_mode)) {
302 return true;
303 }
304 }
305 return false;
306 }
307
MountBindEngFile(const char * source,const char * target)308 static void MountBindEngFile(const char *source, const char *target)
309 {
310 char targetFullPath[PATH_MAX] = {0};
311 const char *p = source;
312 char *q = NULL;
313 const char *end = source + strlen(source);
314
315 if (*p != '/') { // source must start with '/'
316 return;
317 }
318
319 // Get next '/'
320 q = strchr(p + 1, '/');
321 if (q == NULL) {
322 INIT_LOGI("path \' %s \' without extra slash, ignore it", source);
323 return;
324 }
325
326 if (*(end - 1) == '/') {
327 INIT_LOGI("path \' %s \' ends with slash, ignore it", source);
328 return;
329 }
330 // OK, now get sub dir and combine it with target
331 int ret = snprintf_s(targetFullPath, PATH_MAX, PATH_MAX - 1, "%s%s", strcmp(target, "/") == 0 ? "" : target, q);
332 if (ret == -1) {
333 INIT_LOGE("Failed to build target path");
334 return;
335 }
336 INIT_LOGI("target full path is %s", targetFullPath);
337 if (access(targetFullPath, F_OK) != 0) { // file not exist, symlink targetFullPath
338 if (symlink(source, targetFullPath) < 0) {
339 INIT_LOGE("Failed to link %s to %s, err = %d", source, targetFullPath, errno);
340 }
341 return;
342 }
343 if (IsRegularFile(targetFullPath)) { // file exist, moung bind targetFullPath
344 if (mount(source, targetFullPath, NULL, MS_BIND, NULL) != 0) {
345 INIT_LOGE("Failed to bind mount %s to %s, err = %d", source, targetFullPath, errno);
346 } else {
347 INIT_LOGI("Bind mount %s to %s done", source, targetFullPath);
348 }
349 return;
350 }
351 INIT_LOGW("%s without expected type, skip overlay", targetFullPath);
352 }
353
EngFilesOverlay(const char * source,const char * target)354 static void EngFilesOverlay(const char *source, const char *target)
355 {
356 DIR *dir = NULL;
357 struct dirent *de = NULL;
358
359 if ((dir = opendir(source)) == NULL) {
360 INIT_LOGE("Open path \' %s \' failed. err = %d", source, errno);
361 return;
362 }
363 int dfd = dirfd(dir);
364 char srcPath[PATH_MAX] = {};
365 while ((de = readdir(dir)) != NULL) {
366 if (de->d_name[0] == '.') {
367 continue;
368 }
369 if (snprintf_s(srcPath, PATH_MAX, PATH_MAX - 1, "%s/%s", source, de->d_name) == -1) {
370 INIT_LOGE("Failed to build path for overlaying");
371 break;
372 }
373
374 // Determine file type
375 struct stat st = {};
376 if (fstatat(dfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
377 continue;
378 }
379 if (S_ISDIR(st.st_mode)) {
380 EngFilesOverlay(srcPath, target);
381 } else if (S_ISREG(st.st_mode)) {
382 MountBindEngFile(srcPath, target);
383 } else { // Ignore any other file types
384 INIT_LOGI("Ignore %s while overlaying", srcPath);
385 }
386 }
387 closedir(dir);
388 dir = NULL;
389 }
390
RemountRofsOverlay(void)391 int RemountRofsOverlay(void)
392 {
393 int lastRemountResult = GetRemountResult();
394 INIT_LOGI("get last remount result is %d.", lastRemountResult);
395 if (lastRemountResult == REMOUNT_SUCC) {
396 return REMOUNT_SUCC;
397 }
398 FILE *fp;
399 struct mntent *mentry = NULL;
400 if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
401 INIT_LOGE("Failed to open /proc/mounts.");
402 return REMOUNT_FAIL;
403 }
404
405 while (NULL != (mentry = getmntent(fp))) {
406 if (IsSkipRemount(*mentry)) {
407 INIT_LOGI("skip remount %s", mentry->mnt_dir);
408 continue;
409 }
410
411 if (strcmp(mentry->mnt_dir, ROOT_MOUNT_DIR) == 0) {
412 if (!DoSystemRemount(mentry)) {
413 endmntent(fp);
414 INIT_LOGE("do system remount failed on %s", mentry->mnt_dir);
415 return REMOUNT_FAIL;
416 }
417 continue;
418 }
419 INIT_LOGI("do remount %s", mentry->mnt_dir);
420 if (!DoRemount(mentry)) {
421 endmntent(fp);
422 INIT_LOGE("do remount failed on %s", mentry->mnt_dir);
423 return REMOUNT_FAIL;
424 }
425 }
426
427 endmntent(fp);
428 SetRemountResultFlag();
429
430 INIT_LOGI("remount system overlay...");
431 EngFilesOverlay("/eng_system", "/");
432 INIT_LOGI("remount chipset overlay...");
433 EngFilesOverlay("/eng_chipset", "/chipset");
434 return REMOUNT_SUCC;
435 }