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