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