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 <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdbool.h>
20 #include <sys/ioctl.h>
21 #include <sys/mount.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 #include "beget_ext.h"
26 #include "fs_manager/fs_manager.h"
27 #include "init_utils.h"
28 #include "securec.h"
29
30 #ifdef __cplusplus
31 #if __cplusplus
32 extern "C" {
33 #endif
34 #endif
35
36 #define FS_MANAGER_BUFFER_SIZE 512
37 #define BLOCK_SIZE_BUFFER (64)
38 #define RESIZE_BUFFER_SIZE 1024
39
IsSupportedFilesystem(const char * fsType)40 bool IsSupportedFilesystem(const char *fsType)
41 {
42 static const char *supportedFilesystem[] = {"ext4", "f2fs", NULL};
43
44 bool supported = false;
45 int index = 0;
46 if (fsType != NULL) {
47 while (supportedFilesystem[index] != NULL) {
48 if (strcmp(supportedFilesystem[index++], fsType) == 0) {
49 supported = true;
50 break;
51 }
52 }
53 }
54 return supported;
55 }
56
ExecCommand(int argc,char ** argv)57 static int ExecCommand(int argc, char **argv)
58 {
59 if (argc == 0 || argv == NULL || argv[0] == NULL) {
60 return -1;
61 }
62 pid_t pid = fork();
63 if (pid < 0) {
64 BEGET_LOGE("Fork new process to format failed: %d", errno);
65 return -1;
66 }
67 if (pid == 0) {
68 execv(argv[0], argv);
69 exit(-1);
70 }
71 int status;
72 waitpid(pid, &status, 0);
73 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
74 BEGET_LOGE("Command %s failed with status %d", argv[0], WEXITSTATUS(status));
75 }
76 return WEXITSTATUS(status);
77 }
78
DoFormat(const char * devPath,const char * fsType)79 int DoFormat(const char *devPath, const char *fsType)
80 {
81 if (devPath == NULL || fsType == NULL) {
82 return -1;
83 }
84
85 if (!IsSupportedFilesystem(fsType)) {
86 BEGET_LOGE("Do not support filesystem \" %s \"", fsType);
87 return -1;
88 }
89 int ret = 0;
90 char blockSizeBuffer[BLOCK_SIZE_BUFFER] = {0};
91 if (strcmp(fsType, "ext4") == 0) {
92 const unsigned int blockSize = 4096;
93 if (snprintf_s(blockSizeBuffer, BLOCK_SIZE_BUFFER, BLOCK_SIZE_BUFFER - 1, "%u", blockSize) == -1) {
94 BEGET_LOGE("Failed to build block size buffer");
95 return -1;
96 }
97 char *formatCmds[] = {
98 "/bin/mke2fs", "-F", "-t", (char *)fsType, "-b", blockSizeBuffer, (char *)devPath, NULL
99 };
100 int argc = ARRAY_LENGTH(formatCmds);
101 char **argv = (char **)formatCmds;
102 ret = ExecCommand(argc, argv);
103 } else if (strcmp(fsType, "f2fs") == 0) {
104 char *formatCmds[] = {
105 "/bin/make_f2fs", (char *)devPath, NULL
106 };
107 int argc = ARRAY_LENGTH(formatCmds);
108 char **argv = (char **)formatCmds;
109 ret = ExecCommand(argc, argv);
110 }
111 return ret;
112 }
113
GetMountStatusForMountPoint(const char * mp)114 MountStatus GetMountStatusForMountPoint(const char *mp)
115 {
116 if (mp == NULL) {
117 return MOUNT_ERROR;
118 }
119 char buffer[FS_MANAGER_BUFFER_SIZE] = {0};
120 const int expectedItems = 6;
121 int count = 0;
122 char **mountItems = NULL;
123 MountStatus status = MOUNT_ERROR;
124 bool found = false;
125
126 FILE *fp = fopen("/proc/mounts", "r");
127 if (fp == NULL) {
128 return status;
129 }
130 while (fgets(buffer, sizeof(buffer) - 1, fp) != NULL) {
131 size_t n = strlen(buffer);
132 if (buffer[n - 1] == '\n') {
133 buffer[n - 1] = '\0';
134 }
135 mountItems = SplitStringExt(buffer, " ", &count, expectedItems);
136 if (mountItems != NULL && count == expectedItems) {
137 // Second item in /proc/mounts is mount point
138 if (strcmp(mountItems[1], mp) == 0) {
139 FreeStringVector(mountItems, count);
140 found = true;
141 break;
142 }
143 FreeStringVector(mountItems, count);
144 }
145 }
146 if (found == true) {
147 status = MOUNT_MOUNTED;
148 } else if (feof(fp) > 0) {
149 status = MOUNT_UMOUNTED;
150 }
151 (void)fclose(fp);
152 fp = NULL;
153 return status;
154 }
155
DoResizeF2fs(const char * device,const unsigned long long size)156 static int DoResizeF2fs(const char* device, const unsigned long long size)
157 {
158 char *file = "/system/bin/resize.f2fs";
159 if (access(file, F_OK) != 0) {
160 BEGET_LOGE("resize.f2fs is not exists.");
161 return -1;
162 }
163
164 int ret = 0;
165 if (size <= 0) {
166 char *cmd[] = {
167 file, "-s", (char *)device, NULL
168 };
169 int argc = ARRAY_LENGTH(cmd);
170 char **argv = (char **)cmd;
171 ret = ExecCommand(argc, argv);
172 } else {
173 unsigned long long realSize = size *
174 ((unsigned long long)RESIZE_BUFFER_SIZE * RESIZE_BUFFER_SIZE / FS_MANAGER_BUFFER_SIZE);
175 char sizeStr[RESIZE_BUFFER_SIZE] = {0};
176 sprintf_s(sizeStr, RESIZE_BUFFER_SIZE, "%llu", realSize);
177 char *cmd[] = {
178 file, "-s", "-t", sizeStr, (char *)device, NULL
179 };
180 int argc = ARRAY_LENGTH(cmd);
181 char **argv = (char **)cmd;
182 ret = ExecCommand(argc, argv);
183 }
184 BEGET_LOGI("resize.f2fs is ending.");
185 return ret;
186 }
187
DoFsckF2fs(const char * device)188 static int DoFsckF2fs(const char* device)
189 {
190 char *file = "/system/bin/fsck.f2fs";
191 if (access(file, F_OK) != 0) {
192 BEGET_LOGE("fsck.f2fs is not exists.");
193 return -1;
194 }
195
196 char *cmd[] = {
197 file, "-a", (char *)device, NULL
198 };
199 int argc = ARRAY_LENGTH(cmd);
200 char **argv = (char **)cmd;
201 BEGET_LOGI("fsck.f2fs is ending.");
202 return ExecCommand(argc, argv);
203 }
204
DoResizeExt(const char * device,const unsigned long long size)205 static int DoResizeExt(const char* device, const unsigned long long size)
206 {
207 char *file = "/system/bin/resize2fs";
208 if (access(file, F_OK) != 0) {
209 BEGET_LOGE("resize2fs is not exists.");
210 return -1;
211 }
212
213 int ret = 0;
214 if (size <= 0) {
215 char *cmd[] = {
216 file, "-f", (char *)device, NULL
217 };
218 int argc = ARRAY_LENGTH(cmd);
219 char **argv = (char **)cmd;
220 ret = ExecCommand(argc, argv);
221 } else {
222 char sizeStr[RESIZE_BUFFER_SIZE] = {0};
223 sprintf_s(sizeStr, RESIZE_BUFFER_SIZE, "%lluM", size);
224 char *cmd[] = {
225 file, "-f", (char *)device, sizeStr, NULL
226 };
227 int argc = ARRAY_LENGTH(cmd);
228 char **argv = (char **)cmd;
229 ret = ExecCommand(argc, argv);
230 }
231 BEGET_LOGI("resize2fs is ending.");
232 return ret;
233 }
234
DoFsckExt(const char * device)235 static int DoFsckExt(const char* device)
236 {
237 char *file = "/system/bin/e2fsck";
238 if (access(file, F_OK) != 0) {
239 BEGET_LOGE("e2fsck is not exists.");
240 return -1;
241 }
242
243 char *cmd[] = {
244 file, "-y", (char *)device, NULL
245 };
246 int argc = ARRAY_LENGTH(cmd);
247 char **argv = (char **)cmd;
248 BEGET_LOGI("e2fsck is ending.");
249 return ExecCommand(argc, argv);
250 }
251
Mount(const char * source,const char * target,const char * fsType,unsigned long flags,const char * data)252 static int Mount(const char *source, const char *target, const char *fsType,
253 unsigned long flags, const char *data)
254 {
255 struct stat st = {};
256 int rc = -1;
257
258 if (source == NULL || target == NULL || fsType == NULL) {
259 BEGET_LOGE("Invalid argment for mount.");
260 return -1;
261 }
262 if (stat(target, &st) != 0 && errno != ENOENT) {
263 BEGET_LOGE("Cannot get stat of \" %s \", err = %d", target, errno);
264 return -1;
265 }
266 if ((st.st_mode & S_IFMT) == S_IFLNK) { // link, delete it.
267 unlink(target);
268 }
269 if (mkdir(target, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
270 if (errno != EEXIST) {
271 BEGET_LOGE("Failed to create dir \" %s \", err = %d", target, errno);
272 return -1;
273 }
274 }
275 errno = 0;
276 while ((rc = mount(source, target, fsType, flags, data)) != 0) {
277 if (errno == EAGAIN) {
278 BEGET_LOGE("Mount %s to %s failed. try again", source, target);
279 continue;
280 }
281 if (errno == EBUSY) {
282 rc = 0;
283 }
284 break;
285 }
286 return rc;
287 }
288
MountOneItem(FstabItem * item)289 int MountOneItem(FstabItem *item)
290 {
291 if (item == NULL) {
292 return -1;
293 }
294 unsigned long mountFlags;
295 char fsSpecificData[FS_MANAGER_BUFFER_SIZE] = {0};
296
297 mountFlags = GetMountFlags(item->mountOptions, fsSpecificData, sizeof(fsSpecificData));
298 if (!IsSupportedFilesystem(item->fsType)) {
299 BEGET_LOGE("Unsupported file system \" %s \"", item->fsType);
300 return 0;
301 }
302 if (FM_MANAGER_WAIT_ENABLED(item->fsManagerFlags)) {
303 WaitForFile(item->deviceName, WAIT_MAX_SECOND);
304 }
305
306 if (strcmp(item->fsType, "f2fs") == 0 && strcmp(item->mountPoint, "/data") == 0) {
307 int ret = DoResizeF2fs(item->deviceName, 0);
308 if (ret != 0) {
309 BEGET_LOGE("Failed to resize.f2fs dir %s , ret = %d", item->deviceName, ret);
310 }
311
312 ret = DoFsckF2fs(item->deviceName);
313 if (ret != 0) {
314 BEGET_LOGE("Failed to fsck.f2fs dir %s , ret = %d", item->deviceName, ret);
315 }
316 } else if (strcmp(item->fsType, "ext4") == 0 && strcmp(item->mountPoint, "/data") == 0) {
317 int ret = DoResizeExt(item->deviceName, 0);
318 if (ret != 0) {
319 BEGET_LOGE("Failed to resize2fs dir %s , ret = %d", item->deviceName, ret);
320 }
321 ret = DoFsckExt(item->deviceName);
322 if (ret != 0) {
323 BEGET_LOGE("Failed to e2fsck dir %s , ret = %d", item->deviceName, ret);
324 }
325 }
326
327 int rc = Mount(item->deviceName, item->mountPoint, item->fsType, mountFlags, fsSpecificData);
328 if (rc != 0) {
329 BEGET_LOGE("Mount %s to %s failed %d", item->deviceName, item->mountPoint, errno);
330 } else {
331 BEGET_LOGI("Mount %s to %s successful", item->deviceName, item->mountPoint);
332 }
333 return rc;
334 }
335
CheckRequiredAndMount(FstabItem * item,bool required)336 int CheckRequiredAndMount(FstabItem *item, bool required)
337 {
338 int rc = 0;
339 if (item == NULL) {
340 return -1;
341 }
342 if (required) { // Mount partition during first startup.
343 if (FM_MANAGER_REQUIRED_ENABLED(item->fsManagerFlags)) {
344 rc = MountOneItem(item);
345 }
346 } else { // Mount partition during second startup.
347 if (!FM_MANAGER_REQUIRED_ENABLED(item->fsManagerFlags)) {
348 rc = MountOneItem(item);
349 }
350 }
351 return rc;
352 }
353
MountAllWithFstab(const Fstab * fstab,bool required)354 int MountAllWithFstab(const Fstab *fstab, bool required)
355 {
356 if (fstab == NULL) {
357 return -1;
358 }
359
360 FstabItem *item = NULL;
361 int rc = -1;
362 for (item = fstab->head; item != NULL; item = item->next) {
363 rc = CheckRequiredAndMount(item, required);
364 if (required && (rc < 0)) { // Init fail to mount in the first stage and exit directly.
365 break;
366 }
367 }
368 return rc;
369 }
370
MountAllWithFstabFile(const char * fstabFile,bool required)371 int MountAllWithFstabFile(const char *fstabFile, bool required)
372 {
373 if (fstabFile == NULL || *fstabFile == '\0') {
374 return -1;
375 }
376 Fstab *fstab = NULL;
377 if ((fstab = ReadFstabFromFile(fstabFile, false)) == NULL) {
378 BEGET_LOGE("[fs_manager][error] Read fstab file \" %s \" failed\n", fstabFile);
379 return -1;
380 }
381
382 int rc = MountAllWithFstab(fstab, required);
383 ReleaseFstab(fstab);
384 fstab = NULL;
385 return rc;
386 }
387
UmountAllWithFstabFile(const char * fstabFile)388 int UmountAllWithFstabFile(const char *fstabFile)
389 {
390 if (fstabFile == NULL || *fstabFile == '\0') {
391 return -1;
392 }
393 Fstab *fstab = NULL;
394 if ((fstab = ReadFstabFromFile(fstabFile, false)) == NULL) {
395 BEGET_LOGE("Read fstab file \" %s \" failed.", fstabFile);
396 return -1;
397 }
398
399 FstabItem *item = NULL;
400 int rc = -1;
401 for (item = fstab->head; item != NULL; item = item->next) {
402 BEGET_LOGI("Umount %s.", item->mountPoint);
403 MountStatus status = GetMountStatusForMountPoint(item->mountPoint);
404 if (status == MOUNT_ERROR) {
405 BEGET_LOGW("Cannot get mount status of mount point \" %s \"", item->mountPoint);
406 continue; // Cannot get mount status, just ignore it and try next one.
407 } else if (status == MOUNT_UMOUNTED) {
408 BEGET_LOGI("Mount point \" %s \" already unmounted. device path: %s, fs type: %s.",
409 item->mountPoint, item->deviceName, item->fsType);
410 continue;
411 } else {
412 rc = umount(item->mountPoint);
413 if (rc == -1) {
414 BEGET_LOGE("Umount %s failed, device path: %s, fs type: %s, err = %d.",
415 item->mountPoint, item->deviceName, item->fsType, errno);
416 } else {
417 BEGET_LOGE("Umount %s successfully.", item->mountPoint);
418 }
419 }
420 }
421 ReleaseFstab(fstab);
422 fstab = NULL;
423 return rc;
424 }
425 #ifdef __cplusplus
426 #if __cplusplus
427 }
428 #endif
429 #endif
430