1 /*
2 * Copyright (c) 2021-2022 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 <ctype.h>
17 #include <libgen.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdbool.h>
22 #include <sys/mount.h>
23 #include <sys/types.h>
24 #include "beget_ext.h"
25 #include "fs_manager/fs_manager.h"
26 #include "init_utils.h"
27 #include "securec.h"
28
29 #ifdef __cplusplus
30 #if __cplusplus
31 extern "C" {
32 #endif
33 #endif
34
35 struct FsManagerFlags {
36 char *name;
37 unsigned int flags;
38 };
39
40 struct MountFlags {
41 char *name;
42 unsigned long flags;
43 };
44
45 static char *g_fscryptPolicy = NULL;
46
ConvertFlags(char * flagBuffer)47 static unsigned int ConvertFlags(char *flagBuffer)
48 {
49 static struct FsManagerFlags fsFlags[] = {
50 {"check", FS_MANAGER_CHECK},
51 {"wait", FS_MANAGER_WAIT},
52 {"required", FS_MANAGER_REQUIRED},
53 {"nofail", FS_MANAGER_NOFAIL},
54 #ifdef SUPPORT_HVB
55 {"hvb", FS_MANAGER_HVB},
56 #endif
57 };
58
59 BEGET_CHECK_RETURN_VALUE(flagBuffer != NULL && *flagBuffer != '\0', 0); // No valid flags.
60 int flagCount = 0;
61 unsigned int flags = 0;
62 const int maxCount = 3;
63 char **vector = SplitStringExt(flagBuffer, ",", &flagCount, maxCount);
64 BEGET_CHECK_RETURN_VALUE(vector != NULL && flagCount != 0, 0);
65 for (size_t i = 0; i < ARRAY_LENGTH(fsFlags); i++) {
66 for (int j = 0; j < flagCount; j++) {
67 if (strcmp(fsFlags[i].name, vector[j]) == 0) {
68 flags |= fsFlags[i].flags;
69 }
70 }
71 }
72 FreeStringVector(vector, flagCount);
73 return flags;
74 }
75
AddToFstab(Fstab * fstab,FstabItem * item)76 static int AddToFstab(Fstab *fstab, FstabItem *item)
77 {
78 if (fstab == NULL || item == NULL) {
79 return -1;
80 }
81 if (fstab->head != NULL) {
82 item->next = fstab->head->next;
83 fstab->head->next = item;
84 } else {
85 fstab->head = item;
86 }
87 return 0;
88 }
89
ReleaseFstabItem(FstabItem * item)90 void ReleaseFstabItem(FstabItem *item)
91 {
92 if (item != NULL) {
93 if (item->deviceName != NULL) {
94 free(item->deviceName);
95 item->deviceName = NULL;
96 }
97
98 if (item->mountPoint != NULL) {
99 free(item->mountPoint);
100 item->mountPoint = NULL;
101 }
102
103 if (item->fsType != NULL) {
104 free(item->fsType);
105 item->fsType = NULL;
106 }
107
108 if (item->mountOptions != NULL) {
109 free(item->mountOptions);
110 item->mountOptions = NULL;
111 }
112
113 free(item);
114 }
115 }
116
ReleaseFstab(Fstab * fstab)117 void ReleaseFstab(Fstab *fstab)
118 {
119 if (fstab != NULL) {
120 FstabItem *item = fstab->head;
121 while (item != NULL) {
122 FstabItem *tmp = item->next;
123 ReleaseFstabItem(item);
124 item = tmp;
125 }
126 free(fstab);
127 fstab = NULL;
128 }
129 }
130
ParseFstabPerLine(char * str,Fstab * fstab,bool procMounts,const char * separator)131 int ParseFstabPerLine(char *str, Fstab *fstab, bool procMounts, const char *separator)
132 {
133 BEGET_CHECK_RETURN_VALUE(str != NULL && fstab != NULL, -1);
134 char *rest = NULL;
135 FstabItem *item = NULL;
136 char *p = NULL;
137
138 if (separator == NULL || *separator == '\0') {
139 BEGET_LOGE("Invalid separator for parsing fstab");
140 return -1;
141 }
142
143 if ((item = (FstabItem *)calloc(1, sizeof(FstabItem))) == NULL) {
144 errno = ENOMEM;
145 BEGET_LOGE("Allocate memory for FS table item failed, err = %d", errno);
146 return -1;
147 }
148
149 do {
150 if ((p = strtok_r(str, separator, &rest)) == NULL) {
151 BEGET_LOGE("Failed to parse block device.");
152 break;
153 }
154 item->deviceName = strdup(p);
155
156 if ((p = strtok_r(NULL, separator, &rest)) == NULL) {
157 BEGET_LOGE("Failed to parse mount point.");
158 break;
159 }
160 item->mountPoint = strdup(p);
161
162 if ((p = strtok_r(NULL, separator, &rest)) == NULL) {
163 BEGET_LOGE("Failed to parse fs type.");
164 break;
165 }
166 item->fsType = strdup(p);
167
168 if ((p = strtok_r(NULL, separator, &rest)) == NULL) {
169 BEGET_LOGE("Failed to parse mount options.");
170 break;
171 }
172 item->mountOptions = strdup(p);
173
174 if ((p = strtok_r(NULL, separator, &rest)) == NULL) {
175 BEGET_LOGE("Failed to parse fs manager flags.");
176 break;
177 }
178 // @fsManagerFlags only for fstab
179 // Ignore it if we read from /proc/mounts
180 if (!procMounts) {
181 item->fsManagerFlags = ConvertFlags(p);
182 } else {
183 item->fsManagerFlags = 0;
184 }
185 return AddToFstab(fstab, item);
186 } while (0);
187
188 ReleaseFstabItem(item);
189 item = NULL;
190 return -1;
191 }
192
ReadFstabFromFile(const char * file,bool procMounts)193 Fstab *ReadFstabFromFile(const char *file, bool procMounts)
194 {
195 char *line = NULL;
196 size_t allocn = 0;
197 ssize_t readn = 0;
198 Fstab *fstab = NULL;
199
200 FILE *fp = NULL;
201 char *realPath = GetRealPath(file);
202 if (realPath != NULL) {
203 fp = fopen(realPath, "r");
204 free(realPath);
205 } else {
206 fp = fopen(file, "r"); // no file system, can not get real path
207 }
208 if (fp == NULL) {
209 BEGET_LOGE("Open %s failed, err = %d", file, errno);
210 return NULL;
211 }
212
213 if ((fstab = (Fstab *)calloc(1, sizeof(Fstab))) == NULL) {
214 BEGET_LOGE("Allocate memory for FS table failed, err = %d", errno);
215 fclose(fp);
216 fp = NULL;
217 return NULL;
218 }
219
220 // Record line number of fstab file
221 size_t ln = 0;
222 while ((readn = getline(&line, &allocn, fp)) != -1) {
223 char *p = NULL;
224 ln++;
225 if (line[readn - 1] == '\n') {
226 line[readn - 1] = '\0';
227 }
228 p = line;
229 while (isspace(*p)) {
230 p++;
231 }
232
233 if (*p == '\0' || *p == '#') {
234 continue;
235 }
236
237 if (ParseFstabPerLine(p, fstab, procMounts, " \t") < 0) {
238 if (errno == ENOMEM) {
239 // Ran out of memory, there is no reason to continue.
240 break;
241 }
242 // If one line in fstab file parsed with a failure. just give a warning
243 // and skip it.
244 BEGET_LOGW("Cannot parse file \" %s \" at line %zu. skip it", file, ln);
245 continue;
246 }
247 }
248 if (line != NULL) {
249 free(line);
250 }
251 (void)fclose(fp);
252 fp = NULL;
253 return fstab;
254 }
255
FindFstabItemForMountPoint(Fstab fstab,const char * mp)256 FstabItem *FindFstabItemForMountPoint(Fstab fstab, const char *mp)
257 {
258 FstabItem *item = NULL;
259 if (mp != NULL) {
260 for (item = fstab.head; item != NULL; item = item->next) {
261 if ((item->mountPoint != NULL) && (strcmp(item->mountPoint, mp) == 0)) {
262 break;
263 }
264 }
265 }
266 return item;
267 }
268
FindFstabItemForPath(Fstab fstab,const char * path)269 FstabItem *FindFstabItemForPath(Fstab fstab, const char *path)
270 {
271 FstabItem *item = NULL;
272
273 if (path == NULL || *path != '/') {
274 return NULL;
275 }
276
277 char tmp[PATH_MAX] = {0};
278 char *dir = NULL;
279 if (strncpy_s(tmp, PATH_MAX - 1, path, strlen(path)) != EOK) {
280 BEGET_LOGE("Failed to copy path.");
281 return NULL;
282 }
283
284 dir = tmp;
285 while (true) {
286 item = FindFstabItemForMountPoint(fstab, dir);
287 if (item != NULL) {
288 break;
289 }
290 dir = dirname(dir);
291 // Reverse walk through path and met "/", just quit.
292 if (dir == NULL || strcmp(dir, "/") == 0) {
293 break;
294 }
295 }
296 return item;
297 }
298
GetFstabFile(char * fileName,size_t size)299 static char *GetFstabFile(char *fileName, size_t size)
300 {
301 if (InUpdaterMode() == 1) {
302 if (strncpy_s(fileName, size, "/etc/fstab.updater", strlen("/etc/fstab.updater")) != 0) {
303 BEGET_LOGE("Failed strncpy_s err=%d", errno);
304 return NULL;
305 }
306 } else {
307 char hardware[MAX_BUFFER_LEN] = {0};
308 char *buffer = ReadFileData("/proc/cmdline");
309 if (buffer == NULL) {
310 BEGET_LOGE("Failed to read \"/proc/cmdline\"");
311 return NULL;
312 }
313 int ret = GetProcCmdlineValue("hardware", buffer, hardware, MAX_BUFFER_LEN);
314 free(buffer);
315 if (ret != 0) {
316 BEGET_LOGE("Failed get hardware from cmdline");
317 return NULL;
318 }
319 if (snprintf_s(fileName, size, size - 1, "/vendor/etc/fstab.%s", hardware) == -1) {
320 BEGET_LOGE("Failed to build fstab file, err=%d", errno);
321 return NULL;
322 }
323 }
324 BEGET_LOGI("fstab file is %s", fileName);
325 return fileName;
326 }
327
GetBlockDeviceByMountPoint(const char * mountPoint,const Fstab * fstab,char * deviceName,int nameLen)328 int GetBlockDeviceByMountPoint(const char *mountPoint, const Fstab *fstab, char *deviceName, int nameLen)
329 {
330 if (fstab == NULL || mountPoint == NULL || *mountPoint == '\0' || deviceName == NULL) {
331 return -1;
332 }
333 FstabItem *item = FindFstabItemForMountPoint(*fstab, mountPoint);
334 if (item == NULL) {
335 BEGET_LOGE("Failed to get fstab item from mount point \" %s \"", mountPoint);
336 return -1;
337 }
338 if (strncpy_s(deviceName, nameLen, item->deviceName, strlen(item->deviceName)) != 0) {
339 BEGET_LOGE("Failed to copy block device name, err=%d", errno);
340 return -1;
341 }
342 return 0;
343 }
344
GetBlockDeviceByName(const char * deviceName,const Fstab * fstab,char * miscDev,size_t size)345 int GetBlockDeviceByName(const char *deviceName, const Fstab *fstab, char* miscDev, size_t size)
346 {
347 for (FstabItem *item = fstab->head; item != NULL; item = item->next) {
348 if (strstr(item->deviceName, deviceName) != NULL) {
349 BEGET_CHECK_RETURN_VALUE(strcpy_s(miscDev, size, item->deviceName) != 0, 0);
350 }
351 }
352 return -1;
353 }
354
355 static const struct MountFlags MOUNT_FLAGS[] = {
356 { "noatime", MS_NOATIME },
357 { "noexec", MS_NOEXEC },
358 { "nosuid", MS_NOSUID },
359 { "nodev", MS_NODEV },
360 { "nodiratime", MS_NODIRATIME },
361 { "ro", MS_RDONLY },
362 { "rw", 0 },
363 { "sync", MS_SYNCHRONOUS },
364 { "remount", MS_REMOUNT },
365 { "bind", MS_BIND },
366 { "rec", MS_REC },
367 { "unbindable", MS_UNBINDABLE },
368 { "private", MS_PRIVATE },
369 { "slave", MS_SLAVE },
370 { "shared", MS_SHARED },
371 { "defaults", 0 },
372 };
373
IsDefaultMountFlags(const char * str)374 static bool IsDefaultMountFlags(const char *str)
375 {
376 bool isDefault = false;
377
378 if (str != NULL) {
379 for (size_t i = 0; i < ARRAY_LENGTH(MOUNT_FLAGS); i++) {
380 if (strcmp(str, MOUNT_FLAGS[i].name) == 0) {
381 isDefault = true;
382 }
383 }
384 }
385 return isDefault;
386 }
387
ParseDefaultMountFlag(const char * str)388 static unsigned long ParseDefaultMountFlag(const char *str)
389 {
390 unsigned long flags = 0;
391
392 if (str != NULL) {
393 for (size_t i = 0; i < ARRAY_LENGTH(MOUNT_FLAGS); i++) {
394 if (strcmp(str, MOUNT_FLAGS[i].name) == 0) {
395 flags = MOUNT_FLAGS[i].flags;
396 break;
397 }
398 }
399 }
400 return flags;
401 }
402
IsFscryptOption(const char * option)403 static bool IsFscryptOption(const char *option)
404 {
405 if (!option) {
406 return false;
407 }
408 char *fscryptPre = "fscrypt=";
409 if (strncmp(option, fscryptPre, strlen(fscryptPre)) == 0) {
410 return true;
411 }
412 return false;
413 }
414
StoreFscryptPolicy(const char * option)415 static void StoreFscryptPolicy(const char *option)
416 {
417 if (option == NULL) {
418 return;
419 }
420 if (g_fscryptPolicy != NULL) {
421 BEGET_LOGW("StoreFscryptPolicy:inited policy is not empty");
422 free(g_fscryptPolicy);
423 }
424 g_fscryptPolicy = strdup(option);
425 if (g_fscryptPolicy == NULL) {
426 BEGET_LOGE("StoreFscryptPolicy:no memory");
427 return;
428 }
429 BEGET_LOGI("StoreFscryptPolicy:store fscrypt policy, %s", option);
430 }
431
LoadFscryptPolicy(char * buf,size_t size)432 int LoadFscryptPolicy(char *buf, size_t size)
433 {
434 BEGET_LOGI("LoadFscryptPolicy start");
435 if (buf == NULL || g_fscryptPolicy == NULL) {
436 BEGET_LOGE("LoadFscryptPolicy:buf or fscrypt policy is empty");
437 return -ENOMEM;
438 }
439 if (size == 0) {
440 BEGET_LOGE("LoadFscryptPloicy:size is invalid");
441 return -EINVAL;
442 }
443 if (strcpy_s(buf, size, g_fscryptPolicy) != 0) {
444 BEGET_LOGE("loadFscryptPolicy:strcmp failed, error = %d", errno);
445 return -EFAULT;
446 }
447 free(g_fscryptPolicy);
448 g_fscryptPolicy = NULL;
449 BEGET_LOGI("LoadFscryptPolicy success");
450
451 return 0;
452 }
453
GetMountFlags(char * mountFlag,char * fsSpecificData,size_t fsSpecificDataSize,const char * mountPoint)454 unsigned long GetMountFlags(char *mountFlag, char *fsSpecificData, size_t fsSpecificDataSize,
455 const char *mountPoint)
456 {
457 unsigned long flags = 0;
458 BEGET_CHECK_RETURN_VALUE(mountFlag != NULL && fsSpecificData != NULL, 0);
459 int flagCount = 0;
460 // Why max count of mount flags is 15?
461 // There are lots for mount flags defined in sys/mount.h
462 // But we only support to parse 15 in @ParseDefaultMountFlags() function
463 // So set default mount flag number to 15.
464 // If the item configured in fstab contains flag over than 15,
465 // @SplitStringExt can handle it and parse them all. but the parse function will drop it.
466 const int maxCount = 15;
467 char **flagsVector = SplitStringExt(mountFlag, ",", &flagCount, maxCount);
468
469 if (flagsVector == NULL || flagCount == 0) {
470 // No flags or something wrong in SplitStringExt,just return.
471 return 0;
472 }
473
474 for (int i = 0; i < flagCount; i++) {
475 char *p = flagsVector[i];
476 if (IsDefaultMountFlags(p)) {
477 flags |= ParseDefaultMountFlag(p);
478 } else {
479 if (IsFscryptOption(p) &&
480 !strncmp(mountPoint, "/data", strlen("/data"))) {
481 StoreFscryptPolicy(p + strlen("fscrypt="));
482 continue;
483 }
484 if (strncat_s(fsSpecificData, fsSpecificDataSize - 1, p, strlen(p)) != EOK) {
485 BEGET_LOGW("Failed to append mount flag \" %s \", ignore it.", p);
486 continue;
487 }
488 if (i == flagCount - 1) { // last flags, do not need to append ','
489 break;
490 }
491 // Combined each mount flag with ','
492 if (strncat_s(fsSpecificData, fsSpecificDataSize - 1, ",", 1) != EOK) {
493 BEGET_LOGW("Failed to append comma");
494 break; // If cannot add ',' to the end of flags, there is not reason to continue.
495 }
496 }
497 }
498
499 FreeStringVector(flagsVector, flagCount);
500 return flags;
501 }
502
GetBlockDevicePath(const char * partName,char * path,size_t size)503 int GetBlockDevicePath(const char *partName, char *path, size_t size)
504 {
505 BEGET_CHECK_RETURN_VALUE(partName != NULL && path != NULL, -1);
506 Fstab *fstab = LoadFstabFromCommandLine();
507 if (fstab == NULL) {
508 BEGET_LOGI("fstab not found from cmdline, try to get it from file");
509 char *fstabFile = GetFstabFile(path, size);
510 BEGET_CHECK_RETURN_VALUE(fstabFile != NULL, -1);
511 fstab = ReadFstabFromFile(fstabFile, false);
512 }
513 BEGET_CHECK_RETURN_VALUE(fstab != NULL, -1);
514 int ret = GetBlockDeviceByMountPoint(partName, fstab, path, size);
515 BEGET_INFO_CHECK(ret == 0, ret = GetBlockDeviceByName(partName, fstab, path, size),
516 "Mount point not found, try to get path by device name.");
517 ReleaseFstab(fstab);
518 return ret;
519 }
520
521 #define OHOS_REQUIRED_MOUNT_PREFIX "ohos.required_mount."
522 /*
523 * Fstab includes block device node, mount point, file system type, MNT_ Flags and options.
524 * We separate them by spaces in fstab.required file, but the separator is '@' in CmdLine.
525 * The prefix "ohos.required_mount." is the flag of required fstab information in CmdLine.
526 * Format as shown below:
527 * <block device>@<mount point>@<fstype>@<mount options>@<fstab options>
528 * e.g.
529 * ohos.required_mount.system=/dev/block/xxx/by-name/system@/usr@ext4@ro,barrier=1@wait,required
530 */
ParseRequiredMountInfo(const char * item,Fstab * fstab)531 static int ParseRequiredMountInfo(const char *item, Fstab *fstab)
532 {
533 char mountOptions[MAX_BUFFER_LEN] = {};
534 char partName[NAME_SIZE] = {};
535 // Sanity checks
536 BEGET_CHECK(!(item == NULL || *item == '\0' || fstab == NULL), return -1);
537
538 char *p = NULL;
539 if ((p = strstr(item, "=")) != NULL) {
540 const char *q = item + strlen(OHOS_REQUIRED_MOUNT_PREFIX); // Get partition name
541 BEGET_CHECK(!(q == NULL || *q == '\0' || (p - q) <= 0), return -1);
542 BEGET_ERROR_CHECK(strncpy_s(partName, NAME_SIZE -1, q, p - q) == EOK,
543 return -1, "Failed to copy required partition name");
544 p++; // skip '='
545 BEGET_ERROR_CHECK(strncpy_s(mountOptions, MAX_BUFFER_LEN -1, p, strlen(p)) == EOK,
546 return -1, "Failed to copy required mount info: %s", item);
547 }
548 BEGET_LOGV("Mount option of partition %s is [%s]", partName, mountOptions);
549 if (ParseFstabPerLine(mountOptions, fstab, false, "@") < 0) {
550 BEGET_LOGE("Failed to parse mount options of partition \' %s \', options: %s", partName, mountOptions);
551 return -1;
552 }
553 return 0;
554 }
555
LoadFstabFromCommandLine(void)556 Fstab* LoadFstabFromCommandLine(void)
557 {
558 Fstab *fstab = NULL;
559 char *cmdline = ReadFileData(BOOT_CMD_LINE);
560 bool isDone = false;
561
562 BEGET_ERROR_CHECK(cmdline != NULL, return NULL, "Read from \'/proc/cmdline\' failed, err = %d", errno);
563 TrimTail(cmdline, '\n');
564 BEGET_ERROR_CHECK((fstab = (Fstab *)calloc(1, sizeof(Fstab))) != NULL, return NULL,
565 "Allocate memory for FS table failed, err = %d", errno);
566 char *start = cmdline;
567 char *end = start + strlen(cmdline);
568 while (start < end) {
569 char *token = strstr(start, " ");
570 if (token == NULL) {
571 break;
572 }
573
574 // Startswith " "
575 if (token == start) {
576 start++;
577 continue;
578 }
579 *token = '\0';
580 if (strncmp(start, OHOS_REQUIRED_MOUNT_PREFIX,
581 strlen(OHOS_REQUIRED_MOUNT_PREFIX)) != 0) {
582 start = token + 1;
583 continue;
584 }
585 isDone = true;
586 if (ParseRequiredMountInfo(start, fstab) < 0) {
587 BEGET_LOGE("Failed to parse \' %s \'", start);
588 isDone = false;
589 break;
590 }
591 start = token + 1;
592 }
593
594 // handle last one
595 if (start < end) {
596 if (strncmp(start, OHOS_REQUIRED_MOUNT_PREFIX,
597 strlen(OHOS_REQUIRED_MOUNT_PREFIX)) == 0 &&
598 ParseRequiredMountInfo(start, fstab) < 0) {
599 BEGET_LOGE("Failed to parse \' %s \'", start);
600 isDone = false;
601 }
602 }
603
604 if (!isDone) {
605 ReleaseFstab(fstab);
606 fstab = NULL;
607 }
608 free(cmdline);
609 return fstab;
610 }
611 #ifdef __cplusplus
612 #if __cplusplus
613 }
614 #endif
615 #endif
616