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
This commit is contained in:
parent
3918ae6c76
commit
fa1c677c1a
5 changed files with 148 additions and 12 deletions
14
Disk.cpp
14
Disk.cpp
|
@ -47,8 +47,10 @@ namespace vold {
|
||||||
static const char* kSgdiskPath = "/system/bin/sgdisk";
|
static const char* kSgdiskPath = "/system/bin/sgdisk";
|
||||||
static const char* kSgdiskToken = " \t\n";
|
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 char* kSysfsMmcMaxMinors = "/sys/module/mmcblk/parameters/perdev_minors";
|
||||||
|
|
||||||
|
static const unsigned int kMajorBlockLoop = 7;
|
||||||
static const unsigned int kMajorBlockScsiA = 8;
|
static const unsigned int kMajorBlockScsiA = 8;
|
||||||
static const unsigned int kMajorBlockScsiB = 65;
|
static const unsigned int kMajorBlockScsiB = 65;
|
||||||
static const unsigned int kMajorBlockScsiC = 66;
|
static const unsigned int kMajorBlockScsiC = 66;
|
||||||
|
@ -229,6 +231,10 @@ status_t Disk::readMetadata() {
|
||||||
|
|
||||||
unsigned int majorId = major(mDevice);
|
unsigned int majorId = major(mDevice);
|
||||||
switch (majorId) {
|
switch (majorId) {
|
||||||
|
case kMajorBlockLoop: {
|
||||||
|
mLabel = "Virtual";
|
||||||
|
break;
|
||||||
|
}
|
||||||
case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
|
case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
|
||||||
case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
|
case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
|
||||||
case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
|
case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
|
||||||
|
@ -534,6 +540,14 @@ int Disk::getMaxMinors() {
|
||||||
// Figure out maximum partition devices supported
|
// Figure out maximum partition devices supported
|
||||||
unsigned int majorId = major(mDevice);
|
unsigned int majorId = major(mDevice);
|
||||||
switch (majorId) {
|
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 kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
|
||||||
case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
|
case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
|
||||||
case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
|
case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
|
||||||
|
|
78
Loop.cpp
78
Loop.cpp
|
@ -32,12 +32,19 @@
|
||||||
|
|
||||||
#include <cutils/log.h>
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/stringprintf.h>
|
||||||
|
#include <android-base/unique_fd.h>
|
||||||
|
|
||||||
#include <sysutils/SocketClient.h>
|
#include <sysutils/SocketClient.h>
|
||||||
#include "Loop.h"
|
#include "Loop.h"
|
||||||
#include "Asec.h"
|
#include "Asec.h"
|
||||||
#include "VoldUtil.h"
|
#include "VoldUtil.h"
|
||||||
#include "sehandle.h"
|
#include "sehandle.h"
|
||||||
|
|
||||||
|
using android::base::StringPrintf;
|
||||||
|
using android::base::unique_fd;
|
||||||
|
|
||||||
int Loop::dumpState(SocketClient *c) {
|
int Loop::dumpState(SocketClient *c) {
|
||||||
int i;
|
int i;
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -229,6 +236,40 @@ int Loop::create(const char *id, const char *loopFile, char *loopDeviceBuffer, s
|
||||||
return 0;
|
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 Loop::destroyByDevice(const char *loopDevice) {
|
||||||
int device_fd;
|
int device_fd;
|
||||||
|
|
||||||
|
@ -254,20 +295,37 @@ int Loop::destroyByFile(const char * /*loopFile*/) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Loop::createImageFile(const char *file, unsigned long numSectors) {
|
int Loop::createImageFile(const char *file, unsigned long numSectors) {
|
||||||
int fd;
|
int res = 0;
|
||||||
|
|
||||||
if ((fd = creat(file, 0600)) < 0) {
|
char* secontext = nullptr;
|
||||||
SLOGE("Error creating imagefile (%s)", strerror(errno));
|
if (sehandle) {
|
||||||
return -1;
|
if (!selabel_lookup(sehandle, &secontext, file, S_IFREG)) {
|
||||||
|
setfscreatecon(secontext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ftruncate(fd, numSectors * 512) < 0) {
|
unique_fd fd(creat(file, 0600));
|
||||||
SLOGE("Error truncating imagefile (%s)", strerror(errno));
|
if (fd.get() == -1) {
|
||||||
close(fd);
|
PLOG(ERROR) << "Failed to create image " << file;
|
||||||
return -1;
|
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) {
|
int Loop::resizeImageFile(const char *file, unsigned long numSectors) {
|
||||||
|
|
2
Loop.h
2
Loop.h
|
@ -17,6 +17,7 @@
|
||||||
#ifndef _LOOP_H
|
#ifndef _LOOP_H
|
||||||
#define _LOOP_H
|
#define _LOOP_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <linux/loop.h>
|
#include <linux/loop.h>
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ public:
|
||||||
static int lookupActive(const char *id, char *buffer, size_t len);
|
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 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 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 destroyByDevice(const char *loopDevice);
|
||||||
static int destroyByFile(const char *loopFile);
|
static int destroyByFile(const char *loopFile);
|
||||||
static int createImageFile(const char *file, unsigned long numSectors);
|
static int createImageFile(const char *file, unsigned long numSectors);
|
||||||
|
|
|
@ -88,7 +88,13 @@ const char *VolumeManager::ASECDIR = "/mnt/asec";
|
||||||
*/
|
*/
|
||||||
const char *VolumeManager::LOOPDIR = "/mnt/obb";
|
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 kMajorBlockMmc = 179;
|
||||||
static const unsigned int kMajorBlockExperimentalMin = 240;
|
static const unsigned int kMajorBlockExperimentalMin = 240;
|
||||||
|
@ -249,6 +255,55 @@ char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
|
||||||
return buffer;
|
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<android::vold::Disk>(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) {
|
int VolumeManager::setDebug(bool enable) {
|
||||||
mDebug = enable;
|
mDebug = enable;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -266,6 +321,9 @@ int VolumeManager::start() {
|
||||||
new android::vold::EmulatedVolume("/data/media"));
|
new android::vold::EmulatedVolume("/data/media"));
|
||||||
mInternalEmulated->create();
|
mInternalEmulated->create();
|
||||||
|
|
||||||
|
// Consider creating a virtual disk
|
||||||
|
updateVirtualDisk();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +513,7 @@ int VolumeManager::onUserStarted(userid_t userId) {
|
||||||
// Note that sometimes the system will spin up processes from Zygote
|
// Note that sometimes the system will spin up processes from Zygote
|
||||||
// before actually starting the user, so we're okay if Zygote
|
// before actually starting the user, so we're okay if Zygote
|
||||||
// already created this directory.
|
// 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);
|
fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
|
||||||
|
|
||||||
mStartedUsers.insert(userId);
|
mStartedUsers.insert(userId);
|
||||||
|
@ -634,6 +692,7 @@ int VolumeManager::reset() {
|
||||||
disk->destroy();
|
disk->destroy();
|
||||||
disk->create();
|
disk->create();
|
||||||
}
|
}
|
||||||
|
updateVirtualDisk();
|
||||||
mAddedUsers.clear();
|
mAddedUsers.clear();
|
||||||
mStartedUsers.clear();
|
mStartedUsers.clear();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -176,6 +176,7 @@ public:
|
||||||
int unmountLoopImage(const char *containerId, const char *loopId,
|
int unmountLoopImage(const char *containerId, const char *loopId,
|
||||||
const char *fileName, const char *mountPoint, bool force);
|
const char *fileName, const char *mountPoint, bool force);
|
||||||
|
|
||||||
|
int updateVirtualDisk();
|
||||||
int setDebug(bool enable);
|
int setDebug(bool enable);
|
||||||
|
|
||||||
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
|
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
|
||||||
|
@ -211,6 +212,8 @@ private:
|
||||||
std::unordered_map<userid_t, int> mAddedUsers;
|
std::unordered_map<userid_t, int> mAddedUsers;
|
||||||
std::unordered_set<userid_t> mStartedUsers;
|
std::unordered_set<userid_t> mStartedUsers;
|
||||||
|
|
||||||
|
std::string mVirtualDiskPath;
|
||||||
|
std::shared_ptr<android::vold::Disk> mVirtualDisk;
|
||||||
std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
|
std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
|
||||||
std::shared_ptr<android::vold::VolumeBase> mPrimary;
|
std::shared_ptr<android::vold::VolumeBase> mPrimary;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue