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/mount.h>
21 #include <sys/stat.h>
22 #include <sys/wait.h>
23 #include <unistd.h>
24 #include <linux/limits.h>
25 #include "beget_ext.h"
26 #include "fs_manager/fs_manager.h"
27 #include "init_utils.h"
28 #include "param/init_param.h"
29 #include "securec.h"
30 #ifdef SUPPORT_HVB
31 #include "dm_verity.h"
32 #endif
33 #include "init_filesystems.h"
34
35 #ifdef __cplusplus
36 #if __cplusplus
37 extern "C" {
38 #endif
39 #endif
40
41 #define FS_MANAGER_BUFFER_SIZE 512
42 #define BLOCK_SIZE_BUFFER (64)
43 #define RESIZE_BUFFER_SIZE 1024
44 const off_t PARTITION_ACTIVE_SLOT_OFFSET = 1024;
45 const off_t PARTITION_ACTIVE_SLOT_SIZE = 4;
46
InitPostMount(const char * mountPoint,int rc)47 __attribute__((weak)) void InitPostMount(const char *mountPoint, int rc)
48 {
49 }
50
51 static const SUPPORTED_FILE_SYSTEM supportedFileSystems[] = {
52 { "ext4", 0 },
53 { "f2fs", 1 },
54 { "overlay", 0 },
55 { NULL, 0 }
56 };
57
58 static void **extendedFileSystems_ = NULL;
59
InitSetExtendedFileSystems(const SUPPORTED_FILE_SYSTEM * extendedFileSystems[])60 void InitSetExtendedFileSystems(const SUPPORTED_FILE_SYSTEM *extendedFileSystems[])
61 {
62 extendedFileSystems_ = (void **)extendedFileSystems;
63 }
64
GetSupportedFileSystemInfo(const char * fsType)65 static const SUPPORTED_FILE_SYSTEM *GetSupportedFileSystemInfo(const char *fsType)
66 {
67 return (const SUPPORTED_FILE_SYSTEM *)OH_ExtendableStrDictGet((void **)supportedFileSystems,
68 sizeof(SUPPORTED_FILE_SYSTEM), fsType, 0, extendedFileSystems_);
69 }
70
IsSupportedDataType(const char * fsType)71 static bool IsSupportedDataType(const char *fsType)
72 {
73 const SUPPORTED_FILE_SYSTEM *item = GetSupportedFileSystemInfo(fsType);
74 if (item == NULL) {
75 return false;
76 }
77 if (item->for_userdata) {
78 return true;
79 }
80 return false;
81 }
82
IsSupportedFilesystem(const char * fsType)83 bool IsSupportedFilesystem(const char *fsType)
84 {
85 const SUPPORTED_FILE_SYSTEM *item = GetSupportedFileSystemInfo(fsType);
86 if (item == NULL) {
87 return false;
88 }
89 return true;
90 }
91
ExecCommand(int argc,char ** argv)92 static int ExecCommand(int argc, char **argv)
93 {
94 BEGET_CHECK(!(argc == 0 || argv == NULL || argv[0] == NULL), return -1);
95
96 BEGET_LOGI("Execute %s begin", argv[0]);
97 pid_t pid = fork();
98 BEGET_ERROR_CHECK(pid >= 0, return -1, "Fork new process to format failed: %d", errno);
99
100 if (pid == 0) {
101 execv(argv[0], argv);
102 exit(-1);
103 }
104 int status;
105 waitpid(pid, &status, 0);
106 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
107 BEGET_LOGE("Command %s failed with status %d", argv[0], WEXITSTATUS(status));
108 }
109 BEGET_LOGI("Execute %s end", argv[0]);
110 return WEXITSTATUS(status);
111 }
112
DoFormat(const char * devPath,const char * fsType)113 int DoFormat(const char *devPath, const char *fsType)
114 {
115 if (devPath == NULL || fsType == NULL) {
116 return -1;
117 }
118
119 if (!IsSupportedFilesystem(fsType)) {
120 BEGET_LOGE("Do not support filesystem \" %s \"", fsType);
121 return -1;
122 }
123 int ret = 0;
124 if (strcmp(fsType, "ext4") == 0) {
125 char blockSizeBuffer[BLOCK_SIZE_BUFFER] = {0};
126 const unsigned int blockSize = 4096;
127 ret = snprintf_s(blockSizeBuffer, BLOCK_SIZE_BUFFER, BLOCK_SIZE_BUFFER - 1, "%u", blockSize);
128 BEGET_ERROR_CHECK(ret != -1, return -1, "Failed to build block size buffer");
129
130 char *formatCmds[] = {
131 "/bin/mke2fs", "-F", "-t", (char *)fsType, "-b", blockSizeBuffer, (char *)devPath, NULL
132 };
133 int argc = ARRAY_LENGTH(formatCmds);
134 char **argv = (char **)formatCmds;
135 ret = ExecCommand(argc, argv);
136 } else if (IsSupportedDataType(fsType)) {
137 #ifdef __MUSL__
138 char *formatCmds[] = {
139 "/bin/mkfs.f2fs", "-d1", "-O", "encrypt", "-O", "quota", "-O", "verity", "-O", "project_quota,extra_attr",
140 "-O", "sb_checksum", (char *)devPath, NULL
141 };
142 #else
143 char *formatCmds[] = {
144 "/bin/make_f2fs", "-d1", "-O", "encrypt", "-O", "quota", "-O", "verity", "-O", "project_quota,extra_attr",
145 "-O", "sb_checksum", (char *)devPath, NULL
146 };
147 #endif
148 int argc = ARRAY_LENGTH(formatCmds);
149 char **argv = (char **)formatCmds;
150 ret = ExecCommand(argc, argv);
151 }
152 return ret;
153 }
154
GetMountStatusForMountPoint(const char * mp)155 MountStatus GetMountStatusForMountPoint(const char *mp)
156 {
157 if (mp == NULL) {
158 return MOUNT_ERROR;
159 }
160 char buffer[FS_MANAGER_BUFFER_SIZE] = {0};
161 const int expectedItems = 6;
162 int count = 0;
163 char **mountItems = NULL;
164 MountStatus status = MOUNT_ERROR;
165 bool found = false;
166
167 FILE *fp = fopen("/proc/mounts", "r");
168 BEGET_CHECK(fp != NULL, return status);
169
170 while (fgets(buffer, sizeof(buffer) - 1, fp) != NULL) {
171 size_t n = strlen(buffer);
172 if (buffer[n - 1] == '\n') {
173 buffer[n - 1] = '\0';
174 }
175 mountItems = SplitStringExt(buffer, " ", &count, expectedItems);
176 if (mountItems != NULL && count == expectedItems) {
177 // Second item in /proc/mounts is mount point
178 if (strcmp(mountItems[1], mp) == 0) {
179 FreeStringVector(mountItems, count);
180 found = true;
181 break;
182 }
183 FreeStringVector(mountItems, count);
184 }
185 }
186 if (found) {
187 status = MOUNT_MOUNTED;
188 } else if (feof(fp) > 0) {
189 status = MOUNT_UMOUNTED;
190 }
191 (void)fclose(fp);
192 fp = NULL;
193 return status;
194 }
195
196 #define MAX_RESIZE_PARAM_NUM 20
DoResizeF2fs(const char * device,const unsigned long long size,const unsigned int fsManagerFlags)197 static int DoResizeF2fs(const char* device, const unsigned long long size, const unsigned int fsManagerFlags)
198 {
199 char *file = "/system/bin/resize.f2fs";
200 char sizeStr[RESIZE_BUFFER_SIZE] = {0};
201 char *argv[MAX_RESIZE_PARAM_NUM] = {NULL};
202 int argc = 0;
203
204 BEGET_ERROR_CHECK(access(file, F_OK) == 0, return -1, "resize.f2fs is not exists.");
205
206 argv[argc++] = file;
207 if (fsManagerFlags & FS_MANAGER_PROJQUOTA) {
208 argv[argc++] = "-O";
209 argv[argc++] = "extra_attr,project_quota";
210 }
211 if (fsManagerFlags & FS_MANAGER_CASEFOLD) {
212 argv[argc++] = "-O";
213 argv[argc++] = "casefold";
214 argv[argc++] = "-C";
215 argv[argc++] = "utf8";
216 }
217 if (fsManagerFlags & FS_MANAGER_COMPRESSION) {
218 argv[argc++] = "-O";
219 argv[argc++] = "extra_attr,compression";
220 }
221 if (fsManagerFlags & FS_MANAGER_DEDUP) {
222 argv[argc++] = "-O";
223 argv[argc++] = "extra_attr,dedup";
224 }
225
226 if (size != 0) {
227 unsigned long long realSize = size *
228 ((unsigned long long)RESIZE_BUFFER_SIZE * RESIZE_BUFFER_SIZE / FS_MANAGER_BUFFER_SIZE);
229 int len = sprintf_s(sizeStr, RESIZE_BUFFER_SIZE, "%llu", realSize);
230 if (len <= 0) {
231 BEGET_LOGE("Write buffer size failed.");
232 }
233 argv[argc++] = "-t";
234 argv[argc++] = sizeStr;
235 }
236
237 argv[argc++] = (char*)device;
238 BEGET_ERROR_CHECK(argc <= MAX_RESIZE_PARAM_NUM, return -1, "argc: %d is too big.", argc);
239 return ExecCommand(argc, argv);
240 }
241
DoFsckF2fs(const char * device)242 static int DoFsckF2fs(const char* device)
243 {
244 char *file = "/system/bin/fsck.f2fs";
245 BEGET_ERROR_CHECK(access(file, F_OK) == 0, return -1, "fsck.f2fs is not exists.");
246
247 char *cmd[] = {
248 file, "-p1", (char *)device, NULL
249 };
250 int argc = ARRAY_LENGTH(cmd);
251 char **argv = (char **)cmd;
252 return ExecCommand(argc, argv);
253 }
254
DoResizeExt(const char * device,const unsigned long long size)255 static int DoResizeExt(const char* device, const unsigned long long size)
256 {
257 char *file = "/system/bin/resize2fs";
258 BEGET_ERROR_CHECK(access(file, F_OK) == 0, return -1, "resize2fs is not exists.");
259
260 int ret = 0;
261 if (size == 0) {
262 char *cmd[] = {
263 file, "-f", (char *)device, NULL
264 };
265 int argc = ARRAY_LENGTH(cmd);
266 char **argv = (char **)cmd;
267 ret = ExecCommand(argc, argv);
268 } else {
269 char sizeStr[RESIZE_BUFFER_SIZE] = {0};
270 int len = sprintf_s(sizeStr, RESIZE_BUFFER_SIZE, "%lluM", size);
271 if (len <= 0) {
272 BEGET_LOGE("Write buffer size failed.");
273 }
274 char *cmd[] = {
275 file, "-f", (char *)device, sizeStr, NULL
276 };
277 int argc = ARRAY_LENGTH(cmd);
278 char **argv = (char **)cmd;
279 ret = ExecCommand(argc, argv);
280 }
281 return ret;
282 }
283
DoFsckExt(const char * device)284 static int DoFsckExt(const char* device)
285 {
286 char *file = "/system/bin/e2fsck";
287 BEGET_ERROR_CHECK(access(file, F_OK) == 0, return -1, "e2fsck is not exists.");
288
289 char *cmd[] = {
290 file, "-y", (char *)device, NULL
291 };
292 int argc = ARRAY_LENGTH(cmd);
293 char **argv = (char **)cmd;
294 return ExecCommand(argc, argv);
295 }
296
Mount(const char * source,const char * target,const char * fsType,unsigned long flags,const char * data)297 static int Mount(const char *source, const char *target, const char *fsType,
298 unsigned long flags, const char *data)
299 {
300 struct stat st = {};
301 int rc = -1;
302
303 bool isTrue = source == NULL || target == NULL || fsType == NULL;
304 BEGET_ERROR_CHECK(!isTrue, return -1, "Invalid argument for mount.");
305
306 isTrue = stat(target, &st) != 0 && errno != ENOENT;
307 BEGET_ERROR_CHECK(!isTrue, return -1, "Cannot get stat of \" %s \", err = %d", target, errno);
308
309 BEGET_CHECK((st.st_mode & S_IFMT) != S_IFLNK, unlink(target)); // link, delete it.
310
311 if (mkdir(target, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
312 BEGET_ERROR_CHECK(errno == EEXIST, return -1, "Failed to create dir \" %s \", err = %d", target, errno);
313 }
314 errno = 0;
315 if ((rc = mount(source, target, fsType, flags, data)) != 0) {
316 BEGET_WARNING_CHECK(errno != EBUSY, rc = 0, "Mount %s to %s busy, ignore", source, target);
317 }
318 return rc;
319 }
320
GetSlotInfoFromCmdLine(const char * slotInfoName)321 static int GetSlotInfoFromCmdLine(const char *slotInfoName)
322 {
323 char value[MAX_BUFFER_LEN] = {0};
324 BEGET_INFO_CHECK(GetParameterFromCmdLine(slotInfoName, value, MAX_BUFFER_LEN) == 0,
325 return -1, "Failed to get %s value from cmdline", slotInfoName);
326 return atoi(value);
327 }
328
GetSlotInfoFromBootctrl(off_t offset,off_t size)329 static int GetSlotInfoFromBootctrl(off_t offset, off_t size)
330 {
331 char bootctrlDev[MAX_BUFFER_LEN] = {0};
332 BEGET_ERROR_CHECK(GetBlockDevicePath("/bootctrl", bootctrlDev, MAX_BUFFER_LEN) == 0,
333 return -1, "Failed to get bootctrl device");
334 char *realPath = GetRealPath(bootctrlDev);
335 BEGET_ERROR_CHECK(realPath != NULL, return -1, "Failed to get bootctrl device real path");
336 int fd = open(realPath, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
337 free(realPath);
338 BEGET_ERROR_CHECK(fd >= 0, return -1, "Failed to open bootctrl device, errno %d", errno);
339 BEGET_ERROR_CHECK(lseek(fd, offset, SEEK_SET) >= 0, close(fd); return -1,
340 "Failed to lseek bootctrl device fd, errno %d", errno);
341 int slotInfo = 0;
342 BEGET_INFO_CHECK(read(fd, &slotInfo, sizeof(slotInfo)) == size, close(fd); return -1,
343 "Failed to read current slot from bootctrl, errno %d", errno);
344 close(fd);
345 return slotInfo;
346 }
347
GetBootSlots(void)348 int GetBootSlots(void)
349 {
350 return GetSlotInfoFromCmdLine("bootslots");
351 }
352
GetCurrentSlot(void)353 int GetCurrentSlot(void)
354 {
355 // get current slot from cmdline
356 int currentSlot = GetSlotInfoFromCmdLine("currentslot");
357 BEGET_CHECK_RETURN_VALUE(currentSlot <= 0, currentSlot);
358 BEGET_LOGI("No valid slot value found from cmdline, try to get it from bootctrl");
359
360 // get current slot from bootctrl
361 return GetSlotInfoFromBootctrl(PARTITION_ACTIVE_SLOT_OFFSET, PARTITION_ACTIVE_SLOT_SIZE);
362 }
363
MountOneItem(FstabItem * item)364 int MountOneItem(FstabItem *item)
365 {
366 if (item == NULL) {
367 return -1;
368 }
369 unsigned long mountFlags;
370 char fsSpecificData[FS_MANAGER_BUFFER_SIZE] = {0};
371
372 mountFlags = GetMountFlags(item->mountOptions, fsSpecificData, sizeof(fsSpecificData),
373 item->mountPoint);
374 if (!IsSupportedFilesystem(item->fsType)) {
375 BEGET_LOGE("Unsupported file system \" %s \"", item->fsType);
376 return 0;
377 }
378 if (FM_MANAGER_WAIT_ENABLED(item->fsManagerFlags)) {
379 WaitForFile(item->deviceName, WAIT_MAX_SECOND);
380 }
381
382 if (strcmp(item->mountPoint, "/data") == 0 && IsSupportedDataType(item->fsType)) {
383 int ret = DoResizeF2fs(item->deviceName, 0, item->fsManagerFlags);
384 if (ret != 0) {
385 BEGET_LOGE("Failed to resize.f2fs dir %s , ret = %d", item->deviceName, ret);
386 }
387
388 ret = DoFsckF2fs(item->deviceName);
389 if (ret != 0) {
390 BEGET_LOGE("Failed to fsck.f2fs dir %s , ret = %d", item->deviceName, ret);
391 }
392 } else if (strcmp(item->fsType, "ext4") == 0 && strcmp(item->mountPoint, "/data") == 0) {
393 int ret = DoResizeExt(item->deviceName, 0);
394 if (ret != 0) {
395 BEGET_LOGE("Failed to resize2fs dir %s , ret = %d", item->deviceName, ret);
396 }
397 ret = DoFsckExt(item->deviceName);
398 if (ret != 0) {
399 BEGET_LOGE("Failed to e2fsck dir %s , ret = %d", item->deviceName, ret);
400 }
401 }
402
403 int retryCount = 3;
404 int rc = 0;
405 while (retryCount-- > 0) {
406 rc = Mount(item->deviceName, item->mountPoint, item->fsType, mountFlags, fsSpecificData);
407 if (rc == 0) {
408 break;
409 }
410 BEGET_LOGE("Mount device %s to %s failed, err = %d, retry", item->deviceName, item->mountPoint, errno);
411 }
412 InitPostMount(item->mountPoint, rc);
413 if (rc != 0) {
414 if (FM_MANAGER_NOFAIL_ENABLED(item->fsManagerFlags)) {
415 BEGET_LOGE("Mount no fail device %s to %s failed, err = %d", item->deviceName, item->mountPoint, errno);
416 } else {
417 BEGET_LOGW("Mount %s to %s failed, err = %d. Ignore failure", item->deviceName, item->mountPoint, errno);
418 rc = 0;
419 }
420 } else {
421 BEGET_LOGI("Mount %s to %s successful", item->deviceName, item->mountPoint);
422 }
423 return rc;
424 }
425
AdjustPartitionNameByPartitionSlot(FstabItem * item)426 static void AdjustPartitionNameByPartitionSlot(FstabItem *item)
427 {
428 BEGET_CHECK_ONLY_RETURN(strstr(item->deviceName, "/system") != NULL ||
429 strstr(item->deviceName, "/vendor") != NULL);
430 char buffer[MAX_BUFFER_LEN] = {0};
431 int slot = GetCurrentSlot();
432 BEGET_ERROR_CHECK(slot > 0 && slot <= MAX_SLOT, slot = 1, "slot value %d is invalid, set default value", slot);
433 BEGET_ERROR_CHECK(sprintf_s(buffer, sizeof(buffer), "%s_%c", item->deviceName, 'a' + slot - 1) > 0,
434 return, "Failed to format partition name suffix, use default partition name");
435 free(item->deviceName);
436 item->deviceName = strdup(buffer);
437 BEGET_LOGI("partition name with slot suffix: %s", item->deviceName);
438 }
439
CheckRequiredAndMount(FstabItem * item,bool required)440 static int CheckRequiredAndMount(FstabItem *item, bool required)
441 {
442 int rc = 0;
443 if (item == NULL) {
444 return -1;
445 }
446 if (required) { // Mount partition during first startup.
447 if (FM_MANAGER_REQUIRED_ENABLED(item->fsManagerFlags)) {
448 int bootSlots = GetBootSlots();
449 BEGET_INFO_CHECK(bootSlots <= 1, AdjustPartitionNameByPartitionSlot(item),
450 "boot slots is %d, now adjust partition name according to current slot", bootSlots);
451 #ifdef SUPPORT_HVB
452 rc = HvbDmVeritySetUp(item);
453 if (rc != 0) {
454 BEGET_LOGE("set dm_verity err, ret = 0x%x", rc);
455 return rc;
456 }
457 #endif
458 rc = MountOneItem(item);
459 }
460 } else { // Mount partition during second startup.
461 if (!FM_MANAGER_REQUIRED_ENABLED(item->fsManagerFlags)) {
462 rc = MountOneItem(item);
463 }
464 }
465 return rc;
466 }
467
MountAllWithFstab(const Fstab * fstab,bool required)468 int MountAllWithFstab(const Fstab *fstab, bool required)
469 {
470 BEGET_CHECK(fstab != NULL, return -1);
471
472 FstabItem *item = NULL;
473 int rc = -1;
474
475 #ifdef SUPPORT_HVB
476 if (required) {
477 rc = HvbDmVerityinit(fstab);
478 if (rc != 0) {
479 BEGET_LOGE("set dm_verity init, ret = 0x%x", rc);
480 return rc;
481 }
482 }
483 #endif
484
485 for (item = fstab->head; item != NULL; item = item->next) {
486 rc = CheckRequiredAndMount(item, required);
487 if (required && (rc < 0)) { // Init fail to mount in the first stage and exit directly.
488 break;
489 }
490 }
491
492 #ifdef SUPPORT_HVB
493 if (required)
494 HvbDmVerityFinal();
495 #endif
496
497 return rc;
498 }
499
MountAllWithFstabFile(const char * fstabFile,bool required)500 int MountAllWithFstabFile(const char *fstabFile, bool required)
501 {
502 bool isFile = fstabFile == NULL || *fstabFile == '\0';
503 BEGET_CHECK(!isFile, return -1);
504
505 Fstab *fstab = NULL;
506 if ((fstab = ReadFstabFromFile(fstabFile, false)) == NULL) {
507 BEGET_LOGE("[fs_manager][error] Read fstab file \" %s \" failed\n", fstabFile);
508 return -1;
509 }
510
511 int rc = MountAllWithFstab(fstab, required);
512 ReleaseFstab(fstab);
513 fstab = NULL;
514 return rc;
515 }
516
UmountAllWithFstabFile(const char * fstabFile)517 int UmountAllWithFstabFile(const char *fstabFile)
518 {
519 bool isFile = fstabFile == NULL || *fstabFile == '\0';
520 BEGET_CHECK(!isFile, return -1);
521
522 Fstab *fstab = NULL;
523 fstab = ReadFstabFromFile(fstabFile, false);
524 BEGET_ERROR_CHECK(fstab != NULL, return -1, "Read fstab file \" %s \" failed.", fstabFile);
525
526 FstabItem *item = NULL;
527 int rc = -1;
528 for (item = fstab->head; item != NULL; item = item->next) {
529 BEGET_LOGI("Umount %s.", item->mountPoint);
530 MountStatus status = GetMountStatusForMountPoint(item->mountPoint);
531 if (status == MOUNT_ERROR) {
532 BEGET_LOGW("Cannot get mount status of mount point \" %s \"", item->mountPoint);
533 continue; // Cannot get mount status, just ignore it and try next one.
534 } else if (status == MOUNT_UMOUNTED) {
535 BEGET_LOGI("Mount point \" %s \" already unmounted. device path: %s, fs type: %s.",
536 item->mountPoint, item->deviceName, item->fsType);
537 continue;
538 } else {
539 rc = umount(item->mountPoint);
540 if (rc == -1) {
541 BEGET_LOGE("Umount %s failed, device path: %s, fs type: %s, err = %d.",
542 item->mountPoint, item->deviceName, item->fsType, errno);
543 } else {
544 BEGET_LOGE("Umount %s successfully.", item->mountPoint);
545 }
546 }
547 }
548 ReleaseFstab(fstab);
549 fstab = NULL;
550 return rc;
551 }
552 #ifdef __cplusplus
553 #if __cplusplus
554 }
555 #endif
556 #endif
557