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