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