/* * __mount(): wrapper around the mount() system call which also * sets the underlying block device to read-only if the mount is read-only. * See "man 2 mount" for return values. */ static int __mount(const char *source, const char *target, const struct fstab_rec *rec) { unsigned long mountflags = rec->flags; int ret; int save_errno; /* We need this because sometimes we have legacy symlinks * that are lingering around and need cleaning up. */ struct stat info; if (!lstat(target, &info)) if ((info.st_mode & S_IFMT) == S_IFLNK) unlink(target); mkdir(target, 0755); ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options); save_errno = errno; INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret); if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { fs_mgr_set_blk_ro(source); } errno = save_errno; return ret; }
// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for // mount. The 'wait_for_verity_dev' parameter makes this function wait for the // verity device to get created before return int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) { int retval = FS_MGR_SETUP_VERITY_FAIL; int fd = -1; std::string verity_blk_name; struct fec_handle *f = NULL; struct fec_verity_metadata verity; struct verity_table_params params = { .table = NULL }; const std::string mount_point(basename(entry->mount_point.c_str())); bool verified_at_boot = false; android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance(); if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) < 0) { PERROR << "Failed to open '" << entry->blk_device << "'"; return retval; } // read verity metadata if (fec_verity_get_metadata(f, &verity) < 0) { PERROR << "Failed to get verity metadata '" << entry->blk_device << "'"; // Allow verity disabled when the device is unlocked without metadata if (fs_mgr_is_device_unlocked()) { retval = FS_MGR_SETUP_VERITY_SKIPPED; LWARNING << "Allow invalid metadata when the device is unlocked"; } goto out; } #ifdef ALLOW_ADBD_DISABLE_VERITY if (verity.disabled) { retval = FS_MGR_SETUP_VERITY_DISABLED; LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG"; goto out; } #endif // read ecc metadata if (fec_ecc_get_metadata(f, ¶ms.ecc) < 0) { params.ecc.valid = false; } params.ecc_dev = entry->blk_device.c_str(); if (load_verity_state(*entry, ¶ms.mode) < 0) { /* if accessing or updating the state failed, switch to the default * safe mode. This makes sure the device won't end up in an endless * restart loop, and no corrupted data will be exposed to userspace * without a warning. */ params.mode = VERITY_MODE_EIO; } if (!verity.table) { goto out; } params.table = strdup(verity.table); if (!params.table) { goto out; } // verify the signature on the table if (verify_verity_signature(verity) < 0) { // Allow signature verification error when the device is unlocked if (fs_mgr_is_device_unlocked()) { retval = FS_MGR_SETUP_VERITY_SKIPPED; LWARNING << "Allow signature verification error when the device is unlocked"; goto out; } if (params.mode == VERITY_MODE_LOGGING) { // the user has been warned, allow mounting without dm-verity retval = FS_MGR_SETUP_VERITY_SKIPPED; goto out; } // invalidate root hash and salt to trigger device-specific recovery if (invalidate_table(params.table, verity.table_length) < 0) { goto out; } } LINFO << "Enabling dm-verity for " << mount_point.c_str() << " (mode " << params.mode << ")"; // Update the verity params using the actual block device path update_verity_table_blk_device(entry->blk_device, ¶ms.table, entry->fs_mgr_flags.slot_select); // load the verity mapping table if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) { goto loaded; } if (params.ecc.valid) { // kernel may not support error correction, try without LINFO << "Disabling error correction for " << mount_point.c_str(); params.ecc.valid = false; if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) { goto loaded; } } // try the legacy format for backwards compatibility if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_legacy_verity_table) == 0) { goto loaded; } if (params.mode != VERITY_MODE_EIO) { // as a last resort, EIO mode should always be supported LINFO << "Falling back to EIO mode for " << mount_point.c_str(); params.mode = VERITY_MODE_EIO; if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_legacy_verity_table) == 0) { goto loaded; } } LERROR << "Failed to load verity table for " << mount_point.c_str(); goto out; loaded: if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) { LERROR << "Couldn't get verity device number!"; goto out; } // mark the underlying block device as read-only fs_mgr_set_blk_ro(entry->blk_device); // Verify the entire partition in one go // If there is an error, allow it to mount as a normal verity partition. if (entry->fs_mgr_flags.verify_at_boot) { LINFO << "Verifying partition " << entry->blk_device << " at boot"; int err = read_partition(verity_blk_name.c_str(), verity.data_size); if (!err) { LINFO << "Verified verity partition " << entry->blk_device << " at boot"; verified_at_boot = true; } } // assign the new verity block device as the block device if (!verified_at_boot) { entry->blk_device = verity_blk_name; } else if (!dm.DeleteDevice(mount_point)) { LERROR << "Failed to remove verity device " << mount_point.c_str(); goto out; } // make sure we've set everything up properly if (wait_for_verity_dev && !fs_mgr_wait_for_file(entry->blk_device, 1s)) { goto out; } retval = FS_MGR_SETUP_VERITY_SUCCESS; out: if (fd != -1) { close(fd); } fec_close(f); free(params.table); return retval; }
/* When multiple fstab records share the same mount_point, it will * try to mount each one in turn, and ignore any duplicates after a * first successful mount. * Returns -1 on error, and FS_MGR_MNTALL_* otherwise. */ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode) { int i = 0; int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE; int error_count = 0; int mret = -1; int mount_errno = 0; int attempted_idx = -1; if (!fstab) { return -1; } for (i = 0; i < fstab->num_entries; i++) { /* Don't mount entries that are managed by vold or not for the mount mode*/ if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) || ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) || ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) { continue; } /* Skip swap and raw partition entries such as boot, recovery, etc */ if (!strcmp(fstab->recs[i].fs_type, "swap") || !strcmp(fstab->recs[i].fs_type, "emmc") || !strcmp(fstab->recs[i].fs_type, "mtd")) { continue; } /* Skip mounting the root partition, as it will already have been mounted */ if (!strcmp(fstab->recs[i].mount_point, "/")) { if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) { fs_mgr_set_blk_ro(fstab->recs[i].blk_device); } continue; } /* Translate LABEL= file system labels into block devices */ if (!strcmp(fstab->recs[i].fs_type, "ext2") || !strcmp(fstab->recs[i].fs_type, "ext3") || !strcmp(fstab->recs[i].fs_type, "ext4")) { int tret = translate_ext_labels(&fstab->recs[i]); if (tret < 0) { ERROR("Could not translate label to block device\n"); continue; } } if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); } if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { int rc = fs_mgr_setup_verity(&fstab->recs[i]); if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) { INFO("Verity disabled"); } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) { ERROR("Could not set up verified partition, skipping!\n"); continue; } } int last_idx_inspected; int top_idx = i; mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); i = last_idx_inspected; mount_errno = errno; /* Deal with encryptability. */ if (!mret) { int status = handle_encryptable(&fstab->recs[attempted_idx]); if (status == FS_MGR_MNTALL_FAIL) { /* Fatal error - no point continuing */ return status; } if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) { if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) { // Log and continue ERROR("Only one encryptable/encrypted partition supported\n"); } encryptable = status; } /* Success! Go get the next one */ continue; } /* mount(2) returned an error, handle the encryptable/formattable case */ bool wiped = partition_wiped(fstab->recs[top_idx].blk_device); bool crypt_footer = false; if (mret && mount_errno != EBUSY && mount_errno != EACCES && fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) { /* top_idx and attempted_idx point at the same partition, but sometimes * at two different lines in the fstab. Use the top one for formatting * as that is the preferred one. */ ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__, fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point, fstab->recs[top_idx].fs_type); if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) && strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) { int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY); if (fd >= 0) { INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc); wipe_block_device(fd, get_file_size(fd)); close(fd); } else { ERROR("%s(): %s wouldn't open (%s)\n", __func__, fstab->recs[top_idx].key_loc, strerror(errno)); } } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) && !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) { crypt_footer = true; } if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) { /* Let's replay the mount actions. */ i = top_idx - 1; continue; } else { ERROR("%s(): Format failed. Suggest recovery...\n", __func__); encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY; continue; } } if (mret && mount_errno != EBUSY && mount_errno != EACCES && fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) { if (wiped) { ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__, fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, fstab->recs[attempted_idx].fs_type); encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY; continue; } else { /* Need to mount a tmpfs at this mountpoint for now, and set * properties that vold will query later for decrypting */ ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__, fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, fstab->recs[attempted_idx].fs_type); if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) { ++error_count; continue; } } encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; } else { if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) { ERROR("Ignoring failure to mount an un-encryptable or wiped partition on" "%s at %s options: %s error: %s\n", fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, fstab->recs[attempted_idx].fs_options, strerror(mount_errno)); } else { ERROR("Failed to mount an un-encryptable or wiped partition on" "%s at %s options: %s error: %s\n", fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, fstab->recs[attempted_idx].fs_options, strerror(mount_errno)); ++error_count; } continue; } } if (error_count) { return -1; } else { return encryptable; } }