From ff1fc9bc417c8a4b6bffb2b12ab69a705676c17a Mon Sep 17 00:00:00 2001 From: Alistair Delva Date: Thu, 14 May 2020 16:35:03 -0700 Subject: [PATCH] Expand virtio_block check to other virtual devices The Android Emulator isn't the only virtual device the virtio-block detection code is useful for, and those platforms might not set any discriminating properties to indicate that they are virtual. Rework the virtio-block major detection to use /proc/devices instead of hardcoding the assumption that any virtual platform can have virtio-block at any experimental major; the new code permits only the exact experimental major assigned to virtio-block. The new code runs everywhere, but it will only run once and could be expanded later to detect dynamic or experimental majors. Bug: 156286088 Change-Id: Ieae805d08fddd0124a397636f04d99194a9ef7e5 --- Utils.cpp | 36 ++++++++++++++++++++++++++++++++++-- Utils.h | 4 ++-- VolumeManager.cpp | 11 ++++------- model/Disk.cpp | 33 ++------------------------------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/Utils.cpp b/Utils.cpp index 1e20d75..b129990 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -77,6 +77,7 @@ bool sSleepOnUnmount = true; static const char* kBlkidPath = "/system/bin/blkid"; static const char* kKeyPath = "/data/misc/vold"; +static const char* kProcDevices = "/proc/devices"; static const char* kProcFilesystems = "/proc/filesystems"; static const char* kAndroidDir = "/Android/"; @@ -1103,8 +1104,39 @@ bool Readlinkat(int dirfd, const std::string& path, std::string* result) { } } -bool IsRunningInEmulator() { - return android::base::GetBoolProperty("ro.kernel.qemu", false); +static unsigned int GetMajorBlockVirtioBlk() { + std::string devices; + if (!ReadFileToString(kProcDevices, &devices)) { + PLOG(ERROR) << "Unable to open /proc/devices"; + return 0; + } + + bool blockSection = false; + for (auto line : android::base::Split(devices, "\n")) { + if (line == "Block devices:") { + blockSection = true; + } else if (line == "Character devices:") { + blockSection = false; + } else if (blockSection) { + auto tokens = android::base::Split(line, " "); + if (tokens.size() == 2 && tokens[1] == "virtblk") { + return std::stoul(tokens[0]); + } + } + } + + return 0; +} + +bool IsVirtioBlkDevice(unsigned int major) { + // Most virtualized platforms expose block devices with the virtio-blk + // block device driver. Unfortunately, this driver does not use a fixed + // major number, but relies on the kernel to assign one from a specific + // range of block majors, which are allocated for "LOCAL/EXPERIMENAL USE" + // per Documentation/devices.txt. This is true even for the latest Linux + // kernel (4.4; see init() in drivers/block/virtio_blk.c). + static unsigned int kMajorBlockVirtioBlk = GetMajorBlockVirtioBlk(); + return kMajorBlockVirtioBlk && major == kMajorBlockVirtioBlk; } static status_t findMountPointsWithPrefix(const std::string& prefix, diff --git a/Utils.h b/Utils.h index 5e6ff1b..e04dcaa 100644 --- a/Utils.h +++ b/Utils.h @@ -155,8 +155,8 @@ status_t RestoreconRecursive(const std::string& path); // TODO: promote to android::base bool Readlinkat(int dirfd, const std::string& path, std::string* result); -/* Checks if Android is running in QEMU */ -bool IsRunningInEmulator(); +// Handles dynamic major assignment for virtio-block +bool IsVirtioBlkDevice(unsigned int major); status_t UnmountTreeWithPrefix(const std::string& prefix); status_t UnmountTree(const std::string& mountPoint); diff --git a/VolumeManager.cpp b/VolumeManager.cpp index c0d0e77..f64f5f6 100644 --- a/VolumeManager.cpp +++ b/VolumeManager.cpp @@ -83,6 +83,7 @@ using android::vold::DeleteDirContents; using android::vold::DeleteDirContentsAndDir; using android::vold::EnsureDirExists; using android::vold::IsFilesystemSupported; +using android::vold::IsVirtioBlkDevice; using android::vold::PrepareAndroidDirs; using android::vold::PrepareAppDirFromRoot; using android::vold::PrivateVolume; @@ -103,8 +104,6 @@ static const std::string kEmptyString(""); static const unsigned int kSizeVirtualDisk = 536870912; static const unsigned int kMajorBlockMmc = 179; -static const unsigned int kMajorBlockExperimentalMin = 240; -static const unsigned int kMajorBlockExperimentalMax = 254; using ScanProcCallback = bool(*)(uid_t uid, pid_t pid, int nsFd, const char* name, void* params); @@ -231,12 +230,10 @@ void VolumeManager::handleBlockEvent(NetlinkEvent* evt) { for (const auto& source : mDiskSources) { if (source->matches(eventPath)) { // For now, assume that MMC and virtio-blk (the latter is - // emulator-specific; see Disk.cpp for details) devices are SD, - // and that everything else is USB + // specific to virtual platforms; see Utils.cpp for details) + // devices are SD, and that everything else is USB int flags = source->getFlags(); - if (major == kMajorBlockMmc || (android::vold::IsRunningInEmulator() && - major >= (int)kMajorBlockExperimentalMin && - major <= (int)kMajorBlockExperimentalMax)) { + if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) { flags |= android::vold::Disk::Flags::kSd; } else { flags |= android::vold::Disk::Flags::kUsb; diff --git a/model/Disk.cpp b/model/Disk.cpp index a4324db..4df4e9d 100644 --- a/model/Disk.cpp +++ b/model/Disk.cpp @@ -73,8 +73,6 @@ static const unsigned int kMajorBlockScsiN = 133; static const unsigned int kMajorBlockScsiO = 134; static const unsigned int kMajorBlockScsiP = 135; static const unsigned int kMajorBlockMmc = 179; -static const unsigned int kMajorBlockExperimentalMin = 240; -static const unsigned int kMajorBlockExperimentalMax = 254; static const unsigned int kMajorBlockDynamicMin = 234; static const unsigned int kMajorBlockDynamicMax = 512; @@ -88,33 +86,6 @@ enum class Table { kGpt, }; -static bool isVirtioBlkDevice(unsigned int major) { - /* - * The new emulator's "ranchu" virtual board no longer includes a goldfish - * MMC-based SD card device; instead, it emulates SD cards with virtio-blk, - * which has been supported by upstream kernel and QEMU for quite a while. - * Unfortunately, the virtio-blk block device driver does not use a fixed - * major number, but relies on the kernel to assign one from a specific - * range of block majors, which are allocated for "LOCAL/EXPERIMENAL USE" - * per Documentation/devices.txt. This is true even for the latest Linux - * kernel (4.4; see init() in drivers/block/virtio_blk.c). - * - * This makes it difficult for vold to detect a virtio-blk based SD card. - * The current solution checks two conditions (both must be met): - * - * a) If the running environment is the emulator; - * b) If the major number is an experimental block device major number (for - * x86/x86_64 3.10 ranchu kernels, virtio-blk always gets major number - * 253, but it is safer to match the range than just one value). - * - * Other conditions could be used, too, e.g. the hardware name should be - * "ranchu", the device's sysfs path should end with "/block/vd[d-z]", etc. - * But just having a) and b) is enough for now. - */ - return IsRunningInEmulator() && major >= kMajorBlockExperimentalMin && - major <= kMajorBlockExperimentalMax; -} - static bool isNvmeBlkDevice(unsigned int major, const std::string& sysPath) { return sysPath.find("nvme") != std::string::npos && major >= kMajorBlockDynamicMin && major <= kMajorBlockDynamicMax; @@ -322,7 +293,7 @@ status_t Disk::readMetadata() { break; } default: { - if (isVirtioBlkDevice(majorId)) { + if (IsVirtioBlkDevice(majorId)) { LOG(DEBUG) << "Recognized experimental block major ID " << majorId << " as virtio-blk (emulator's virtual SD card device)"; mLabel = "Virtual"; @@ -627,7 +598,7 @@ int Disk::getMaxMinors() { return std::stoi(tmp); } default: { - if (isVirtioBlkDevice(majorId)) { + if (IsVirtioBlkDevice(majorId)) { // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is // 2^4 - 1 = 15 return 15;