| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/storage/fshost/block-device-manager.h" |
| |
| #include <fidl/fuchsia.device/cpp/markers.h> |
| #include <fidl/fuchsia.device/cpp/wire.h> |
| #include <inttypes.h> |
| #include <lib/fdio/cpp/caller.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/device/block.h> |
| #include <zircon/hw/gpt.h> |
| |
| #include <set> |
| #include <utility> |
| |
| #include "lib/fdio/directory.h" |
| #include "lib/fidl/llcpp/channel.h" |
| #include "lib/service/llcpp/service.h" |
| #include "src/lib/storage/fs_management/cpp/format.h" |
| #include "src/storage/fshost/block-device-interface.h" |
| #include "src/storage/fshost/constants.h" |
| #include "src/storage/fshost/copier.h" |
| #include "zircon/errors.h" |
| |
| namespace fshost { |
| namespace { |
| |
| // Setting for the maximum bytes to allow a partition to grow to. |
| struct PartitionLimit { |
| // When unset, this limit will apply only to non-ramdisk devices. See |
| // Config::kApplyLimitsToRamdisk. |
| bool apply_to_ramdisk = false; |
| |
| // Partition max size in bytes, 0 means "no limit". |
| uint64_t max_bytes = 0; |
| }; |
| |
| // Splits the path into a directory and the last component. |
| std::pair<std::string_view, std::string_view> SplitPath(std::string_view path) { |
| size_t separator = path.rfind('/'); |
| if (separator != std::string::npos) { |
| return std::make_pair(path.substr(0, separator), path.substr(separator + 1)); |
| } |
| return std::make_pair(std::string_view(), path); |
| } |
| |
| bool IsRamdisk(const BlockDeviceInterface& device) { |
| constexpr std::string_view kRamdiskPrefix = "/dev/sys/platform/00:00:2d/ramctl/"; |
| return device.topological_path().compare(0, kRamdiskPrefix.length(), kRamdiskPrefix) == 0; |
| } |
| |
| // Matches all NAND devices. |
| class NandMatcher : public BlockDeviceManager::Matcher { |
| public: |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| if (device.IsNand()) { |
| return fs_management::kDiskFormatNandBroker; |
| } |
| return fs_management::kDiskFormatUnknown; |
| } |
| |
| zx_status_t Add(BlockDeviceInterface& device) override { |
| zx_status_t status = device.Add(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (path_.empty()) { |
| path_ = device.topological_path(); |
| } |
| return ZX_OK; |
| } |
| |
| const std::string& path() const { return path_; } |
| |
| private: |
| std::string path_; |
| }; |
| |
| // Matches anything that appears to have the given content and keeps track of the first device it |
| // finds. |
| class ContentMatcher : public BlockDeviceManager::Matcher { |
| public: |
| // If |allow_multiple| is true, multiple devices will be matched. Otherwise, only the first |
| // device that appears will match. |
| ContentMatcher(fs_management::DiskFormat format, bool allow_multiple) |
| : format_(format), allow_multiple_(allow_multiple) {} |
| |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| if (!allow_multiple_ && !path_.empty()) { |
| // Only match the first occurrence. |
| return fs_management::kDiskFormatUnknown; |
| } |
| if (device.content_format() == format_) { |
| return format_; |
| } |
| return fs_management::kDiskFormatUnknown; |
| } |
| |
| zx_status_t Add(BlockDeviceInterface& device) override { |
| zx_status_t status = device.Add(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (path_.empty()) { |
| path_ = device.topological_path(); |
| } |
| return ZX_OK; |
| } |
| |
| const std::string& path() const { return path_; } |
| |
| private: |
| const fs_management::DiskFormat format_; |
| const bool allow_multiple_; |
| std::string path_; |
| }; |
| |
| // Matches devices that handle groups of partitions. |
| class PartitionMapMatcher : public ContentMatcher { |
| public: |
| // |suffix| is a device that is expected to appear when the driver is bound. For example, FVM, |
| // will add a "/fvm" device before adding children whilst GPT won't add anything. If |
| // |ramdisk_required| is set, this matcher will only match against a ram-disk. |
| PartitionMapMatcher(fs_management::DiskFormat format, bool allow_multiple, |
| std::string_view suffix, bool ramdisk_required) |
| : ContentMatcher(format, allow_multiple), |
| suffix_(suffix), |
| ramdisk_required_(ramdisk_required) {} |
| |
| bool ramdisk_required() const { return ramdisk_required_; } |
| |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| if (ramdisk_required_ && !IsRamdisk(device)) { |
| return fs_management::kDiskFormatUnknown; |
| } |
| return ContentMatcher::Match(device); |
| } |
| |
| // Returns true if |device| is a child of the device matched by this matcher. |
| bool IsChild(const BlockDeviceInterface& device) const { |
| if (path().empty()) { |
| return false; |
| } |
| // Child partitions should have topological paths of the form: |
| // .../<suffix>/<partition-name>/block |
| auto [dir1, base1] = SplitPath(device.topological_path()); |
| if (base1 != "block") { |
| return false; |
| } |
| auto [dir2, base2] = SplitPath(dir1); |
| // base should be something like <partition-name>-p-1, but we ignore that. |
| return path() + suffix_ == dir2; |
| } |
| |
| private: |
| const std::string suffix_; |
| const bool ramdisk_required_; |
| }; |
| |
| // Extracts the path that the FVM driver responds to FIDL requests at given the PartitionMapMatcher |
| // for the path. |
| std::string GetFvmPathForPartitionMap(const PartitionMapMatcher& matcher) { |
| return matcher.path() + "/fvm"; |
| } |
| |
| // Matches a partition with a given name and expected type GUID. |
| class SimpleMatcher : public BlockDeviceManager::Matcher { |
| public: |
| SimpleMatcher(PartitionMapMatcher& map, std::string partition_name, |
| const fuchsia_hardware_block_partition::wire::Guid& type_guid, |
| fs_management::DiskFormat format, PartitionLimit limit) |
| : map_(map), |
| partition_name_(std::move(partition_name)), |
| type_guid_(type_guid), |
| format_(format), |
| limit_(limit) {} |
| |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| if (map_.IsChild(device) && device.partition_name() == partition_name_ && |
| !memcmp(&device.GetTypeGuid(), &type_guid_, sizeof(type_guid_))) { |
| return format_; |
| } |
| return fs_management::kDiskFormatUnknown; |
| } |
| |
| zx_status_t Add(BlockDeviceInterface& device) override { |
| if (limit_.max_bytes) { |
| if (limit_.apply_to_ramdisk || !IsRamdisk(device)) { |
| // Set the max size for this partition in FVM. Ignore failures since the max size is |
| // mostly a guard rail against bad behavior and we can still function. |
| auto status = |
| device.SetPartitionMaxSize(GetFvmPathForPartitionMap(map_), limit_.max_bytes); |
| ZX_DEBUG_ASSERT(status == ZX_OK); |
| } |
| } |
| return device.Add(); |
| } |
| |
| private: |
| const PartitionMapMatcher& map_; |
| const std::string partition_name_; |
| const fuchsia_hardware_block_partition::wire::Guid type_guid_; |
| const fs_management::DiskFormat format_; |
| const PartitionLimit limit_; |
| }; |
| |
| constexpr std::string_view kZxcryptSuffix = "/zxcrypt/unsealed/block"; |
| |
| // Matches Fxfs partitions and manages migrations that may need to happen, e.g. removing zxcrypt |
| // from beneath Fxfs or migrating from a zxcrypt+minfs partition. |
| class FxfsMatcher : public BlockDeviceManager::Matcher { |
| public: |
| using PartitionNames = std::set<std::string, std::less<>>; |
| |
| FxfsMatcher(const PartitionMapMatcher& map, PartitionNames partition_names, |
| const fuchsia_hardware_block_partition::wire::Guid& type_guid, PartitionLimit limit, |
| bool format_on_corruption) |
| : map_(map), |
| partition_names_(std::move(partition_names)), |
| type_guid_(type_guid), |
| limit_(limit), |
| format_on_corruption_(format_on_corruption) {} |
| |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| bool is_child = |
| zxcrypt_parent_path_.empty() |
| ? map_.IsChild(device) |
| : device.topological_path() == zxcrypt_parent_path_ + std::string(kZxcryptSuffix); |
| if (!is_child || memcmp(&device.GetTypeGuid(), &type_guid_, sizeof(type_guid_)) != 0 || |
| partition_names_.find(device.partition_name()) == partition_names_.end()) { |
| return fs_management::kDiskFormatUnknown; |
| } |
| // We don't actually want to mount a zxcrypt-contained data partition, but we need to extract |
| // any data stored therein (to support paving flows which currently only create zxcrypt+minfs |
| // partitions). When we find a zxcrypt-formatted data partition, we will bind it, pull the data |
| // off, and then reformat to Fxfs (without zxcrypt). |
| if (device.content_format() == fs_management::kDiskFormatZxcrypt) { |
| if (!zxcrypt_parent_path_.empty()) { |
| FX_LOGS(WARNING) << "Unexpectedly found nested zxcrypt devices. Not proceeding."; |
| return fs_management::kDiskFormatUnknown; |
| } |
| return fs_management::kDiskFormatZxcrypt; |
| } |
| return fs_management::kDiskFormatFxfs; |
| } |
| |
| zx_status_t Add(BlockDeviceInterface& device) override { |
| if (limit_.max_bytes) { |
| if (limit_.apply_to_ramdisk || !IsRamdisk(device)) { |
| // Set the max size for this partition in FVM. This is not persisted so we need to set it |
| // every time on mount. Ignore failures since the max size is mostly a guard rail against |
| // bad behavior and we can still function. |
| auto status = |
| device.SetPartitionMaxSize(GetFvmPathForPartitionMap(map_), limit_.max_bytes); |
| ZX_DEBUG_ASSERT(status == ZX_OK); |
| } |
| } |
| if (device.GetFormat() == fs_management::kDiskFormatZxcrypt) { |
| // The channel needs to be cloned before Add is called, since BlockDevice::Add consumes the |
| // device channel for zxcrypt. |
| zxcrypt_parent_path_ = device.topological_path(); |
| return device.Add(format_on_corruption_); |
| } |
| if (zxcrypt_parent_path_.empty()) { |
| return device.Add(format_on_corruption_); |
| } |
| // Copy the data out of the child device. |
| FX_LOGS(INFO) << "Copying data out of " << device.topological_path(); |
| auto copier_or = device.ExtractData(); |
| Copier copied_data; |
| if (copier_or.is_error()) { |
| FX_LOGS(WARNING) << "Failed to copy data out from old partition: " |
| << copier_or.status_string() << ". Reformatting. Expect data loss!"; |
| } else { |
| copied_data = std::move(*copier_or); |
| } |
| // Once we have done so, tear down the zxcrypt device so that we can use it for Fxfs. |
| FX_LOGS(INFO) << "Shutting down zxcrypt..."; |
| auto controller_or = service::Connect<fuchsia_device::Controller>(zxcrypt_parent_path_.c_str()); |
| if (controller_or.is_error()) { |
| FX_LOGS(ERROR) << "Failed to connect to zcxrypt: " << controller_or.status_string(); |
| return ZX_ERR_BAD_STATE; |
| } |
| auto resp = fidl::WireCall(*controller_or)->UnbindChildren(); |
| zx_status_t status = resp.status(); |
| if (status != ZX_OK) { |
| FX_LOGS(WARNING) << "Failed to send UnbindChildren: " << zx_status_get_string(status); |
| return ZX_ERR_BAD_STATE; |
| } |
| if (resp->is_error()) { |
| FX_LOGS(WARNING) << "UnbindChildren failed: " << zx_status_get_string(resp->error_value()); |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| FX_LOGS(INFO) << "Shut down zxcrypt. Re-adding device " << zxcrypt_parent_path_; |
| auto parent_or = device.OpenBlockDevice(zxcrypt_parent_path_.c_str()); |
| if (parent_or.is_error()) { |
| FX_LOGS(WARNING) << "Failed to open parent: " << parent_or.status_string(); |
| return ZX_ERR_BAD_STATE; |
| } |
| zxcrypt_parent_path_.clear(); |
| parent_or->AddData(std::move(copied_data)); |
| parent_or->SetFormat(fs_management::DiskFormat::kDiskFormatFxfs); |
| return parent_or->Add(); |
| } |
| |
| private: |
| const PartitionMapMatcher& map_; |
| const PartitionNames partition_names_; |
| const fuchsia_hardware_block_partition::wire::Guid type_guid_; |
| const PartitionLimit limit_; |
| const bool format_on_corruption_; |
| |
| // Set to the topological path of the block device containing zxcrypt once it's been bound. |
| std::string zxcrypt_parent_path_; |
| }; |
| |
| // Matches a data partition, which is a mutable filesystem (e.g. minfs) optionally backed by |
| // zxcrypt. |
| // Note that Fxfs partitions are matched by FxfsMatcher. |
| class DataPartitionMatcher : public BlockDeviceManager::Matcher { |
| public: |
| using PartitionNames = std::set<std::string, std::less<>>; |
| enum class ZxcryptVariant { |
| // A regular data partition backed by zxcrypt. |
| kNormal, |
| // A data partition not backed by zxcrypt. |
| kNoZxcrypt, |
| // Only attach and unseal the zxcrypt partition; doesn't mount the filesystem. |
| kZxcryptOnly |
| }; |
| |
| struct Variant { |
| ZxcryptVariant zxcrypt = ZxcryptVariant::kNormal; |
| fs_management::DiskFormat format = fs_management::kDiskFormatMinfs; |
| bool format_data_on_corruption = true; |
| }; |
| |
| DataPartitionMatcher(const PartitionMapMatcher& map, PartitionNames partition_names, |
| std::string_view preferred_name, |
| const fuchsia_hardware_block_partition::wire::Guid& type_guid, |
| Variant variant, PartitionLimit limit) |
| : map_(map), |
| partition_names_(std::move(partition_names)), |
| preferred_name_(preferred_name), |
| type_guid_(type_guid), |
| variant_(variant), |
| limit_(limit) {} |
| |
| static Variant GetVariantFromConfig(const fshost_config::Config& config) { |
| Variant variant; |
| if (config.no_zxcrypt()) { |
| variant.zxcrypt = ZxcryptVariant::kNoZxcrypt; |
| } else { |
| variant.zxcrypt = ZxcryptVariant::kNormal; |
| } |
| |
| if (!config.data_filesystem_format().empty()) |
| variant.format = fs_management::DiskFormatFromString(config.data_filesystem_format()); |
| |
| variant.format_data_on_corruption = config.format_data_on_corruption(); |
| return variant; |
| } |
| |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| if (expected_inner_path_.empty()) { |
| if (map_.IsChild(device) && !memcmp(&device.GetTypeGuid(), &type_guid_, sizeof(type_guid_))) { |
| if (partition_names_.find(device.partition_name()) == partition_names_.end()) { |
| FX_LOGS(INFO) << "Ignoring data partition with label '" << device.partition_name() << "'"; |
| return fs_management::kDiskFormatUnknown; |
| } |
| switch (variant_.zxcrypt) { |
| case ZxcryptVariant::kNormal: |
| return map_.ramdisk_required() ? variant_.format : fs_management::kDiskFormatZxcrypt; |
| case ZxcryptVariant::kNoZxcrypt: |
| return variant_.format; |
| case ZxcryptVariant::kZxcryptOnly: |
| return fs_management::kDiskFormatZxcrypt; |
| } |
| } |
| } else if (variant_.zxcrypt == ZxcryptVariant::kNormal && |
| device.topological_path() == expected_inner_path_ && |
| !memcmp(&device.GetTypeGuid(), &type_guid_, sizeof(type_guid_))) { |
| return variant_.format; |
| } |
| return fs_management::kDiskFormatUnknown; |
| } |
| |
| zx_status_t Add(BlockDeviceInterface& device) override { |
| if (limit_.max_bytes) { |
| if (limit_.apply_to_ramdisk || !IsRamdisk(device)) { |
| // Set the max size for this partition in FVM. This is not persisted so we need to set it |
| // every time on mount. Ignore failures since the max size is mostly a guard rail against |
| // bad behavior and we can still function. |
| auto status = |
| device.SetPartitionMaxSize(GetFvmPathForPartitionMap(map_), limit_.max_bytes); |
| ZX_DEBUG_ASSERT(status == ZX_OK); |
| } |
| } |
| |
| if (expected_inner_path_.empty() && !preferred_name_.empty() && |
| device.partition_name() != preferred_name_) { |
| if (zx_status_t status = |
| device.SetPartitionName(GetFvmPathForPartitionMap(map_), preferred_name_); |
| status != ZX_OK) { |
| FX_LOGS(ERROR) << "Failed to change data partition name to '" << preferred_name_ |
| << "': " << zx_status_get_string(status); |
| // Continue since not fatal... |
| } else { |
| FX_LOGS(INFO) << "Changed data partition name to '" << preferred_name_ << "'"; |
| } |
| } |
| |
| // If the volume doesn't appear to be zxcrypt, assume that it's because it was never formatted |
| // as such, or the keys have been shredded, so skip straight to reformatting. Strictly |
| // speaking, it's not necessary, because attempting to unseal should trigger the same |
| // behaviour, but the log messages in that case are scary. |
| if (device.GetFormat() == fs_management::kDiskFormatZxcrypt) { |
| if (device.content_format() != fs_management::kDiskFormatZxcrypt) { |
| FX_LOGS(INFO) << "Formatting as zxcrypt partition"; |
| zx_status_t status = device.FormatZxcrypt(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // Set the reformat_ flag so that when the Minfs device appears we can skip straight to |
| // reformatting it (and skip any fsck). Again, this isn't strictly required because |
| // mounting should fail and we'll reformat, but we can skip that when we know we need to |
| // reformat. |
| reformat_ = true; |
| } |
| } else if (reformat_) { |
| // We formatted zxcrypt, so skip straight to formatting the filesystem. |
| zx_status_t status = device.FormatFilesystem(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| reformat_ = false; |
| } |
| zx_status_t status = device.Add(variant_.format_data_on_corruption); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (device.GetFormat() == fs_management::kDiskFormatZxcrypt) { |
| expected_inner_path_ = device.topological_path(); |
| expected_inner_path_.append(kZxcryptSuffix); |
| } |
| return ZX_OK; |
| } |
| |
| private: |
| const PartitionMapMatcher& map_; |
| const PartitionNames partition_names_; |
| const std::string preferred_name_; |
| const fuchsia_hardware_block_partition::wire::Guid type_guid_; |
| const Variant variant_; |
| const PartitionLimit limit_; |
| |
| // Once we have matched a zxcrypt partition, this field will be set to the expected topological |
| // path of the child device, which will then be matched against directly. |
| std::string expected_inner_path_; |
| // If we reformat the zxcrypt device, this flag is set so that we know we should reformat the |
| // minfs device when it appears. |
| bool reformat_ = false; |
| }; |
| |
| // Matches the factory partition. |
| class FactoryfsMatcher : public BlockDeviceManager::Matcher { |
| public: |
| static constexpr std::string_view kVerityMutableSuffix = "/verity/mutable/block"; |
| static constexpr std::string_view kVerityVerifiedSuffix = "/verity/verified/block"; |
| |
| explicit FactoryfsMatcher(const PartitionMapMatcher& map) : map_(map) {} |
| |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| static constexpr fuchsia_hardware_block_partition::wire::Guid factory_type_guid = |
| GPT_FACTORY_TYPE_GUID; |
| if (base_path_.empty()) { |
| if (map_.IsChild(device) && |
| !memcmp(&device.GetTypeGuid(), &factory_type_guid, sizeof(factory_type_guid)) && |
| device.partition_name() == "factory") { |
| return fs_management::kDiskFormatBlockVerity; |
| } |
| } else if (!memcmp(&device.GetTypeGuid(), &factory_type_guid, sizeof(factory_type_guid)) && |
| (device.topological_path() == std::string(base_path_).append(kVerityMutableSuffix) || |
| device.topological_path() == |
| std::string(base_path_).append(kVerityVerifiedSuffix))) { |
| return fs_management::kDiskFormatFactoryfs; |
| } |
| return fs_management::kDiskFormatUnknown; |
| } |
| |
| zx_status_t Add(BlockDeviceInterface& device) override { |
| zx_status_t status = device.Add(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| base_path_ = device.topological_path(); |
| return ZX_OK; |
| } |
| |
| private: |
| const PartitionMapMatcher& map_; |
| std::string base_path_; |
| }; |
| |
| // Matches devices that report flags with BLOCK_FLAG_BOOTPART set. |
| class BootpartMatcher : public BlockDeviceManager::Matcher { |
| public: |
| fs_management::DiskFormat Match(const BlockDeviceInterface& device) override { |
| fuchsia_hardware_block_BlockInfo info; |
| zx_status_t status = device.GetInfo(&info); |
| if (status != ZX_OK) { |
| return fs_management::kDiskFormatUnknown; |
| } |
| return info.flags & BLOCK_FLAG_BOOTPART ? fs_management::kDiskFormatBootpart |
| : fs_management::kDiskFormatUnknown; |
| } |
| }; |
| |
| DataPartitionMatcher::PartitionNames GetDataPartitionNames(bool include_legacy) { |
| if (include_legacy) { |
| return {std::string(kDataPartitionLabel), "minfs", "fuchsia-data"}; |
| } |
| return {std::string(kDataPartitionLabel)}; |
| } |
| |
| } // namespace |
| |
| BlockDeviceManager::BlockDeviceManager(const fshost_config::Config* config) : config_(*config) { |
| static constexpr fuchsia_hardware_block_partition::wire::Guid data_type_guid = GUID_DATA_VALUE; |
| |
| if (config_.bootpart()) { |
| matchers_.push_back(std::make_unique<BootpartMatcher>()); |
| } |
| if (config_.nand()) { |
| matchers_.push_back(std::make_unique<NandMatcher>()); |
| } |
| |
| auto gpt = |
| std::make_unique<PartitionMapMatcher>(fs_management::kDiskFormatGpt, config_.gpt_all(), "", |
| /*ramdisk_required=*/false); |
| auto fvm = std::make_unique<PartitionMapMatcher>( |
| fs_management::kDiskFormatFvm, /*allow_multiple=*/false, "/fvm", config_.fvm_ramdisk()); |
| |
| bool gpt_required = config_.gpt() || config_.gpt_all(); |
| bool fvm_required = config_.fvm(); |
| |
| // Maximum partition limits. The limits only apply to physical devices (not ramdisks) unless |
| // apply_limits_to_ramdisk is set. |
| PartitionLimit blobfs_limit{.apply_to_ramdisk = config_.apply_limits_to_ramdisk(), |
| .max_bytes = config_.blobfs_max_bytes()}; |
| PartitionLimit data_limit{.apply_to_ramdisk = config_.apply_limits_to_ramdisk(), |
| .max_bytes = config_.data_max_bytes()}; |
| |
| if (!config_.netboot()) { |
| // GPT partitions: |
| if (config_.durable()) { |
| static constexpr fuchsia_hardware_block_partition::wire::Guid durable_type_guid = |
| GPT_DURABLE_TYPE_GUID; |
| matchers_.push_back(std::make_unique<DataPartitionMatcher>( |
| *gpt, DataPartitionMatcher::PartitionNames{GPT_DURABLE_NAME}, std::string_view(), |
| durable_type_guid, DataPartitionMatcher::GetVariantFromConfig(config_), |
| PartitionLimit())); |
| gpt_required = true; |
| } |
| if (config_.factory()) { |
| matchers_.push_back(std::make_unique<FactoryfsMatcher>(*gpt)); |
| gpt_required = true; |
| } |
| |
| // FVM partitions: |
| if (config_.blobfs()) { |
| static constexpr fuchsia_hardware_block_partition::wire::Guid blobfs_type_guid = |
| GUID_BLOB_VALUE; |
| matchers_.push_back(std::make_unique<SimpleMatcher>( |
| *fvm, std::string(kBlobfsPartitionLabel), blobfs_type_guid, |
| fs_management::kDiskFormatBlobfs, blobfs_limit)); |
| fvm_required = true; |
| } |
| if (config_.data()) { |
| if (config_.data_filesystem_format() == "fxfs") { |
| matchers_.push_back(std::make_unique<FxfsMatcher>( |
| *fvm, GetDataPartitionNames(config_.allow_legacy_data_partition_names()), |
| data_type_guid, data_limit, config_.format_data_on_corruption())); |
| } else { |
| matchers_.push_back(std::make_unique<DataPartitionMatcher>( |
| *fvm, GetDataPartitionNames(config_.allow_legacy_data_partition_names()), |
| kDataPartitionLabel, data_type_guid, |
| DataPartitionMatcher::GetVariantFromConfig(config_), data_limit)); |
| } |
| fvm_required = true; |
| } |
| } |
| |
| // The partition map matchers go last because they match on content. |
| if (fvm_required) { |
| std::unique_ptr<PartitionMapMatcher> non_ramdisk_fvm; |
| if (config_.fvm_ramdisk()) { |
| // Add another matcher for the non-ramdisk version of FVM. |
| non_ramdisk_fvm = std::make_unique<PartitionMapMatcher>(fs_management::kDiskFormatFvm, |
| /*allow_multiple=*/false, "/fvm", |
| /*ramdisk_required=*/false); |
| |
| if (config_.zxcrypt_non_ramdisk()) { |
| matchers_.push_back(std::make_unique<DataPartitionMatcher>( |
| *non_ramdisk_fvm, GetDataPartitionNames(config_.allow_legacy_data_partition_names()), |
| kDataPartitionLabel, data_type_guid, |
| DataPartitionMatcher::Variant{.zxcrypt = |
| DataPartitionMatcher::ZxcryptVariant::kZxcryptOnly}, |
| data_limit)); |
| } |
| } |
| matchers_.push_back(std::move(fvm)); |
| if (non_ramdisk_fvm) { |
| matchers_.push_back(std::move(non_ramdisk_fvm)); |
| } |
| } |
| if (gpt_required) { |
| matchers_.push_back(std::move(gpt)); |
| } |
| if (config_.mbr()) { |
| // Default to allowing multiple devices because mbr support is disabled by default and if |
| // it's enabled, it's likely required for removable devices and so supporting multiple |
| // devices is probably appropriate. |
| matchers_.push_back(std::make_unique<PartitionMapMatcher>(fs_management::kDiskFormatMbr, |
| /*allow_multiple=*/true, "", |
| /*ramdisk_required=*/false)); |
| } |
| } |
| |
| zx_status_t BlockDeviceManager::AddDevice(BlockDeviceInterface& device) { |
| if (device.topological_path().empty()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| FX_LOGS(INFO) << "Device " << device.topological_path() << " has content format " |
| << fs_management::DiskFormatString(device.content_format()); |
| for (auto& matcher : matchers_) { |
| fs_management::DiskFormat format = matcher->Match(device); |
| if (format != fs_management::kDiskFormatUnknown) { |
| FX_LOGS(INFO) << "Device " << device.topological_path() << " matched format " |
| << fs_management::DiskFormatString(format); |
| device.SetFormat(format); |
| return matcher->Add(device); |
| } |
| } |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| } // namespace fshost |