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  }