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