1 /*
2 * Copyright (c) 2021 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 "switch_root.h"
17 #include <errno.h>
18 #include <dirent.h>
19 #include <limits.h>
20 #include <fcntl.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include "init_log.h"
26 #include "fs_manager/fs_manager.h"
27 #include "securec.h"
28 #include "init_utils.h"
29
FreeOldRoot(DIR * dir,dev_t dev)30 static void FreeOldRoot(DIR *dir, dev_t dev)
31 {
32 if (dir == NULL) {
33 return;
34 }
35 int dfd = dirfd(dir);
36 bool isDir = false;
37 struct dirent *de = NULL;
38 while ((de = readdir(dir)) != NULL) {
39 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
40 continue;
41 }
42 isDir = false;
43 if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
44 struct stat st = {};
45 if (fstatat(dfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
46 INIT_LOGE("Failed to get stat of %s", de->d_name);
47 continue;
48 }
49
50 if (st.st_dev != dev) {
51 continue; // Not the same device, ignore.
52 }
53 if (!S_ISDIR(st.st_mode)) {
54 continue;
55 }
56 int fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
57 isDir = true;
58 if (fd < 0) {
59 continue;
60 }
61 DIR *subDir = fdopendir(fd);
62 if (subDir != NULL) {
63 FreeOldRoot(subDir, dev);
64 closedir(subDir);
65 } else {
66 close(fd);
67 }
68 }
69 if (unlinkat(dfd, de->d_name, isDir ? AT_REMOVEDIR : 0) < 0) {
70 INIT_LOGE("Failed to unlink %s, err = %d", de->d_name, errno);
71 }
72 }
73 }
74
75 // For sub mountpoint under /dev, /sys, /proc
76 // There is no need to move them individually
77 // We will find a better solution to take care of
78 // all sub mount tree in the future.
UnderBasicMountPoint(const char * path)79 static bool UnderBasicMountPoint(const char *path)
80 {
81 unsigned int i;
82 if (path == NULL || *path == '\0') {
83 return false;
84 }
85 const char *basicMountPoint[] = {"/dev/", "/sys/", "/proc/"};
86 for (i = 0; i < ARRAY_LENGTH(basicMountPoint); i++) {
87 if (strncmp(path, basicMountPoint[i], strlen(basicMountPoint[i])) == 0) {
88 return true;
89 }
90 }
91 return false;
92 }
93
MountToNewTarget(const char * target)94 static int MountToNewTarget(const char *target)
95 {
96 if (target == NULL || *target == '\0') {
97 return -1;
98 }
99 Fstab *fstab = ReadFstabFromFile("/proc/mounts", true);
100 if (fstab == NULL) {
101 INIT_LOGE("Fatal error. Read mounts info from \" /proc/mounts \" failed");
102 return -1;
103 }
104
105 for (FstabItem *item = fstab->head; item != NULL; item = item->next) {
106 const char *mountPoint = item->mountPoint;
107 if (mountPoint == NULL || strcmp(mountPoint, "/") == 0 ||
108 strcmp(mountPoint, target) == 0) {
109 continue;
110 }
111 char newMountPoint[PATH_MAX] = {0};
112 if (snprintf_s(newMountPoint, PATH_MAX, PATH_MAX - 1, "%s%s", target, mountPoint) == -1) {
113 INIT_LOGW("Cannot build new mount point for old mount point \" %s \"", mountPoint);
114 // Just ignore this one or return error?
115 continue;
116 }
117 INIT_LOGV("new mount point is: %s", newMountPoint);
118 if (!UnderBasicMountPoint(mountPoint)) {
119 INIT_LOGV("Move mount %s to %s", mountPoint, newMountPoint);
120 if (mount(mountPoint, newMountPoint, NULL, MS_MOVE, NULL) < 0) {
121 INIT_LOGE("Failed to mount moving %s to %s, err = %d", mountPoint, newMountPoint, errno);
122 // If one mount entry cannot move to new mountpoint, umount it.
123 umount2(mountPoint, MNT_FORCE);
124 continue;
125 }
126 }
127 }
128 ReleaseFstab(fstab);
129 fstab = NULL;
130 return 0;
131 }
132
FreeRootDir(DIR * oldRoot,dev_t dev)133 static void FreeRootDir(DIR *oldRoot, dev_t dev)
134 {
135 if (oldRoot != NULL) {
136 FreeOldRoot(oldRoot, dev);
137 closedir(oldRoot);
138 }
139 }
140
141 // Switch root from ramdisk to system
SwitchRoot(const char * newRoot)142 int SwitchRoot(const char *newRoot)
143 {
144 if (newRoot == NULL || *newRoot == '\0') {
145 errno = EINVAL;
146 INIT_LOGE("Fatal error. Try to switch to new root with invalid new mount point");
147 return -1;
148 }
149
150 struct stat oldRootStat = {};
151 INIT_ERROR_CHECK(stat("/", &oldRootStat) == 0, return -1, "Failed to get old root \"/\" stat");
152 DIR *oldRoot = opendir("/");
153 INIT_ERROR_CHECK(oldRoot != NULL, return -1, "Failed to open root dir \"/\"");
154 struct stat newRootStat = {};
155 if (stat(newRoot, &newRootStat) != 0) {
156 INIT_LOGE("Failed to get new root \" %s \" stat", newRoot);
157 FreeRootDir(oldRoot, oldRootStat.st_dev);
158 return -1;
159 }
160
161 if (oldRootStat.st_dev == newRootStat.st_dev) {
162 INIT_LOGW("Try to switch root in same device, skip switching root");
163 FreeRootDir(oldRoot, oldRootStat.st_dev);
164 return 0;
165 }
166 if (MountToNewTarget(newRoot) < 0) {
167 INIT_LOGE("Failed to move mount to new root \" %s \" stat", newRoot);
168 FreeRootDir(oldRoot, oldRootStat.st_dev);
169 return -1;
170 }
171 // OK, we've done move mount.
172 // Now mount new root.
173 if (chdir(newRoot) < 0) {
174 INIT_LOGE("Failed to change directory to %s, err = %d", newRoot, errno);
175 FreeRootDir(oldRoot, oldRootStat.st_dev);
176 return -1;
177 }
178
179 if (mount(newRoot, "/", NULL, MS_MOVE, NULL) < 0) {
180 INIT_LOGE("Failed to mount moving %s to %s, err = %d", newRoot, "/", errno);
181 FreeRootDir(oldRoot, oldRootStat.st_dev);
182 return -1;
183 }
184
185 if (chroot(".") < 0) {
186 INIT_LOGE("Failed to change root directory");
187 FreeRootDir(oldRoot, oldRootStat.st_dev);
188 return -1;
189 }
190 FreeRootDir(oldRoot, oldRootStat.st_dev);
191 INIT_LOGI("SwitchRoot to %s finish", newRoot);
192 return 0;
193 }
194