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
31extern "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)
38struct FsManagerFlags {
39    char *name;
40    unsigned int flags;
41};
42
43struct MountFlags {
44    char *name;
45    unsigned long flags;
46};
47
48static char *g_fscryptPolicy = NULL;
49
50static 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
84static 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
98void 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
125void 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
140int 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
190Fstab *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
250FstabItem *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
263FstabItem *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
293static 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
316int 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
333int 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
343static 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
362static 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
376static 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
391static 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
403static 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
429int 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
451unsigned 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
500int 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 */
528static 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
553Fstab* 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