From fa1c677c1ac6fae00653d41cf85ff9951d57f7b1 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Sat, 25 Mar 2017 22:49:13 -0600 Subject: [PATCH] Add a new "virtual disk" feature. It's extremely difficult to test storage related logic on devices that don't have physical SD card slots. So to support better debugging and testing, add a new "virtual disk" feature which mounts a 512MB file through loop device. It relies on the kernel having the "loop.max_part" value set to something other than 0 via the boot command line, since that allows all the existing partition logic to fall into place. Bug: 34903607 Test: builds, boots, virtual disk works Change-Id: I04c5b33e37319d867542985a56b7999a9b7cf35d --- Disk.cpp | 14 +++++++++ Loop.cpp | 78 +++++++++++++++++++++++++++++++++++++++++------ Loop.h | 2 ++ VolumeManager.cpp | 63 ++++++++++++++++++++++++++++++++++++-- VolumeManager.h | 3 ++ 5 files changed, 148 insertions(+), 12 deletions(-) diff --git a/Disk.cpp b/Disk.cpp index 6562d96..8e58be0 100644 --- a/Disk.cpp +++ b/Disk.cpp @@ -47,8 +47,10 @@ namespace vold { static const char* kSgdiskPath = "/system/bin/sgdisk"; static const char* kSgdiskToken = " \t\n"; +static const char* kSysfsLoopMaxMinors = "/sys/module/loop/parameters/max_part"; static const char* kSysfsMmcMaxMinors = "/sys/module/mmcblk/parameters/perdev_minors"; +static const unsigned int kMajorBlockLoop = 7; static const unsigned int kMajorBlockScsiA = 8; static const unsigned int kMajorBlockScsiB = 65; static const unsigned int kMajorBlockScsiC = 66; @@ -229,6 +231,10 @@ status_t Disk::readMetadata() { unsigned int majorId = major(mDevice); switch (majorId) { + case kMajorBlockLoop: { + mLabel = "Virtual"; + break; + } case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH: case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL: @@ -534,6 +540,14 @@ int Disk::getMaxMinors() { // Figure out maximum partition devices supported unsigned int majorId = major(mDevice); switch (majorId) { + case kMajorBlockLoop: { + std::string tmp; + if (!ReadFileToString(kSysfsLoopMaxMinors, &tmp)) { + LOG(ERROR) << "Failed to read max minors"; + return -errno; + } + return atoi(tmp.c_str()); + } case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH: case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL: diff --git a/Loop.cpp b/Loop.cpp index 1127817..7e243de 100644 --- a/Loop.cpp +++ b/Loop.cpp @@ -32,12 +32,19 @@ #include +#include +#include +#include + #include #include "Loop.h" #include "Asec.h" #include "VoldUtil.h" #include "sehandle.h" +using android::base::StringPrintf; +using android::base::unique_fd; + int Loop::dumpState(SocketClient *c) { int i; int fd; @@ -229,6 +236,40 @@ int Loop::create(const char *id, const char *loopFile, char *loopDeviceBuffer, s return 0; } +int Loop::create(const std::string& target, std::string& out_device) { + unique_fd ctl_fd(open("/dev/loop-control", O_RDWR)); + if (ctl_fd.get() == -1) { + PLOG(ERROR) << "Failed to open loop-control"; + return -errno; + } + + int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE); + if (num == -1) { + PLOG(ERROR) << "Failed LOOP_CTL_GET_FREE"; + return -errno; + } + + out_device = StringPrintf("/dev/block/loop%d", num); + + unique_fd target_fd(open(target.c_str(), O_RDWR)); + if (target_fd.get() == -1) { + PLOG(ERROR) << "Failed to open " << target; + return -errno; + } + unique_fd device_fd(open(out_device.c_str(), O_RDWR)); + if (device_fd.get() == -1) { + PLOG(ERROR) << "Failed to open " << out_device; + return -errno; + } + + if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get()) == -1) { + PLOG(ERROR) << "Failed to LOOP_SET_FD"; + return -errno; + } + + return 0; +} + int Loop::destroyByDevice(const char *loopDevice) { int device_fd; @@ -254,20 +295,37 @@ int Loop::destroyByFile(const char * /*loopFile*/) { } int Loop::createImageFile(const char *file, unsigned long numSectors) { - int fd; + int res = 0; - if ((fd = creat(file, 0600)) < 0) { - SLOGE("Error creating imagefile (%s)", strerror(errno)); - return -1; + char* secontext = nullptr; + if (sehandle) { + if (!selabel_lookup(sehandle, &secontext, file, S_IFREG)) { + setfscreatecon(secontext); + } } - if (ftruncate(fd, numSectors * 512) < 0) { - SLOGE("Error truncating imagefile (%s)", strerror(errno)); - close(fd); - return -1; + unique_fd fd(creat(file, 0600)); + if (fd.get() == -1) { + PLOG(ERROR) << "Failed to create image " << file; + res = -errno; + goto done; } - close(fd); - return 0; + + if (fallocate(fd.get(), 0, 0, numSectors * 512) == -1) { + PLOG(WARNING) << "Failed to fallocate; falling back to ftruncate"; + if (ftruncate(fd, numSectors * 512) == -1) { + PLOG(ERROR) << "Failed to ftruncate"; + res = -errno; + } + } + +done: + if (secontext) { + setfscreatecon(nullptr); + freecon(secontext); + } + + return res; } int Loop::resizeImageFile(const char *file, unsigned long numSectors) { diff --git a/Loop.h b/Loop.h index 72130b0..5d8f427 100644 --- a/Loop.h +++ b/Loop.h @@ -17,6 +17,7 @@ #ifndef _LOOP_H #define _LOOP_H +#include #include #include @@ -29,6 +30,7 @@ public: static int lookupActive(const char *id, char *buffer, size_t len); static int lookupInfo(const char *loopDevice, struct asec_superblock *sb, unsigned long *nr_sec); static int create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len); + static int create(const std::string& file, std::string& out_device); static int destroyByDevice(const char *loopDevice); static int destroyByFile(const char *loopFile); static int createImageFile(const char *file, unsigned long numSectors); diff --git a/VolumeManager.cpp b/VolumeManager.cpp index 40e3921..a67c8ec 100644 --- a/VolumeManager.cpp +++ b/VolumeManager.cpp @@ -88,7 +88,13 @@ const char *VolumeManager::ASECDIR = "/mnt/asec"; */ const char *VolumeManager::LOOPDIR = "/mnt/obb"; -static const char* kUserMountPath = "/mnt/user"; +static const char* kPathUserMount = "/mnt/user"; +static const char* kPathVirtualDisk = "/data/misc/vold/virtual_disk"; + +static const char* kPropVirtualDisk = "persist.sys.virtual_disk"; + +/* 512MiB is large enough for testing purposes */ +static const unsigned int kSizeVirtualDisk = 536870912; static const unsigned int kMajorBlockMmc = 179; static const unsigned int kMajorBlockExperimentalMin = 240; @@ -249,6 +255,55 @@ char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) { return buffer; } +int VolumeManager::updateVirtualDisk() { + if (property_get_bool(kPropVirtualDisk, false)) { + if (access(kPathVirtualDisk, F_OK) != 0) { + Loop::createImageFile(kPathVirtualDisk, kSizeVirtualDisk / 512); + } + + if (mVirtualDisk == nullptr) { + if (Loop::create(kPathVirtualDisk, mVirtualDiskPath) != 0) { + LOG(ERROR) << "Failed to create virtual disk"; + return -1; + } + + struct stat buf; + if (stat(mVirtualDiskPath.c_str(), &buf) < 0) { + PLOG(ERROR) << "Failed to stat " << mVirtualDiskPath; + return -1; + } + + auto disk = new android::vold::Disk("virtual", buf.st_rdev, "virtual", + android::vold::Disk::Flags::kAdoptable | android::vold::Disk::Flags::kSd); + disk->create(); + mVirtualDisk = std::shared_ptr(disk); + mDisks.push_back(mVirtualDisk); + } + } else { + if (mVirtualDisk != nullptr) { + dev_t device = mVirtualDisk->getDevice(); + + auto i = mDisks.begin(); + while (i != mDisks.end()) { + if ((*i)->getDevice() == device) { + (*i)->destroy(); + i = mDisks.erase(i); + } else { + ++i; + } + } + + Loop::destroyByDevice(mVirtualDiskPath.c_str()); + mVirtualDisk = nullptr; + } + + if (access(kPathVirtualDisk, F_OK) == 0) { + unlink(kPathVirtualDisk); + } + } + return 0; +} + int VolumeManager::setDebug(bool enable) { mDebug = enable; return 0; @@ -266,6 +321,9 @@ int VolumeManager::start() { new android::vold::EmulatedVolume("/data/media")); mInternalEmulated->create(); + // Consider creating a virtual disk + updateVirtualDisk(); + return 0; } @@ -455,7 +513,7 @@ int VolumeManager::onUserStarted(userid_t userId) { // Note that sometimes the system will spin up processes from Zygote // before actually starting the user, so we're okay if Zygote // already created this directory. - std::string path(StringPrintf("%s/%d", kUserMountPath, userId)); + std::string path(StringPrintf("%s/%d", kPathUserMount, userId)); fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT); mStartedUsers.insert(userId); @@ -634,6 +692,7 @@ int VolumeManager::reset() { disk->destroy(); disk->create(); } + updateVirtualDisk(); mAddedUsers.clear(); mStartedUsers.clear(); return 0; diff --git a/VolumeManager.h b/VolumeManager.h index dd9f09d..796a91d 100644 --- a/VolumeManager.h +++ b/VolumeManager.h @@ -176,6 +176,7 @@ public: int unmountLoopImage(const char *containerId, const char *loopId, const char *fileName, const char *mountPoint, bool force); + int updateVirtualDisk(); int setDebug(bool enable); void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; } @@ -211,6 +212,8 @@ private: std::unordered_map mAddedUsers; std::unordered_set mStartedUsers; + std::string mVirtualDiskPath; + std::shared_ptr mVirtualDisk; std::shared_ptr mInternalEmulated; std::shared_ptr mPrimary; };