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