Progress towards dynamic storage support.

Wire up new Disk and VolumeBase objects and events to start replacing
older DirectVolume code.  Use filesystem UUID as visible PublicVolume
name to be more deterministic.

When starting, create DiskSource instances based on fstab, and watch
for kernel devices to appear.  Turn matching devices into Disk
objects, scan for partitions, and create any relevant VolumeBase
objects.  Broadcast all of these events towards userspace so the
framework can decide what to mount.

Keep track of the primary VolumeBase, and update the new per-user
/storage/self/primary symlink for all started users.

Provide a reset command that framework uses to start from a known
state when runtime is restarted.  When vold is unexpectedly killed,
try recovering by unmounting everything under /mnt and /storage
before moving forward.

Remove UMS sharing support for now, since no current devices support
it; MTP is the recommended solution going forward because it offers
better multi-user support.

Switch killProcessesWithOpenFiles() to directly take signal.  Fix
one SOCK_CLOEXEC bug, but SELinux says there are more lurking.

Bug: 19993667
Change-Id: I2dad1303aa4667ec14c52f774e2a28b3c1c1ff6d
gugelfrei
Jeff Sharkey 9 years ago
parent 2a8c10965a
commit 36801cccf2

@ -34,6 +34,7 @@
#include "CommandListener.h"
#include "VolumeManager.h"
#include "VolumeBase.h"
#include "ResponseCode.h"
#include "Process.h"
#include "Loop.h"
@ -86,6 +87,14 @@ void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
#endif
int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
if (!cond) {
return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
} else {
return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
}
}
CommandListener::DumpCmd::DumpCmd() :
VoldCommand("dump") {
}
@ -129,99 +138,93 @@ int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
}
VolumeManager *vm = VolumeManager::Instance();
int rc = 0;
if (!strcmp(argv[1], "list")) {
bool broadcast = argc >= 3 && !strcmp(argv[2], "broadcast");
return vm->listVolumes(cli, broadcast);
} else if (!strcmp(argv[1], "debug")) {
if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false);
return 0;
}
vm->setDebug(!strcmp(argv[2], "on") ? true : false);
} else if (!strcmp(argv[1], "mount")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);
return 0;
}
rc = vm->mountVolume(argv[2]);
} else if (!strcmp(argv[1], "unmount")) {
if (argc < 3 || argc > 4 ||
((argc == 4 && strcmp(argv[3], "force")) &&
(argc == 4 && strcmp(argv[3], "force_and_revert")))) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false);
return 0;
}
// TODO: tease out methods not directly related to volumes
bool force = false;
bool revert = false;
if (argc >= 4 && !strcmp(argv[3], "force")) {
force = true;
} else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) {
force = true;
revert = true;
}
rc = vm->unmountVolume(argv[2], force, revert);
} else if (!strcmp(argv[1], "format")) {
if (argc < 3 || argc > 4 ||
(argc == 4 && strcmp(argv[3], "wipe"))) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path> [wipe]", false);
return 0;
}
bool wipe = false;
if (argc >= 4 && !strcmp(argv[3], "wipe")) {
wipe = true;
std::string cmd(argv[1]);
if (cmd == "reset") {
return sendGenericOkFail(cli, vm->reset());
} else if (cmd == "shutdown") {
return sendGenericOkFail(cli, vm->shutdown());
} else if (cmd == "partition" && argc > 3) {
// partition [diskId] [public|private|mixed] [ratio]
std::string id(argv[2]);
auto disk = vm->findDisk(id);
if (disk == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
}
rc = vm->formatVolume(argv[2], wipe);
} else if (!strcmp(argv[1], "share")) {
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: volume share <path> <method>", false);
return 0;
std::string type(argv[3]);
if (type == "public") {
return sendGenericOkFail(cli, disk->partitionPublic());
} else if (type == "private") {
return sendGenericOkFail(cli, disk->partitionPrivate());
} else if (type == "mixed") {
if (argc < 4) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
int frac = atoi(argv[4]);
return sendGenericOkFail(cli, disk->partitionMixed(frac));
} else {
return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
rc = vm->shareVolume(argv[2], argv[3]);
} else if (!strcmp(argv[1], "unshare")) {
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: volume unshare <path> <method>", false);
return 0;
} else if (cmd == "mkdirs" && argc > 2) {
// mkdirs [path]
return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
} else if (cmd == "start_user" && argc > 2) {
// start_user [user]
return sendGenericOkFail(cli, vm->startUser(atoi(argv[2])));
} else if (cmd == "cleanup_user" && argc > 2) {
// cleanup_user [user]
return sendGenericOkFail(cli, vm->cleanupUser(atoi(argv[2])));
} else if (cmd == "mount" && argc > 2) {
// mount [volId] [flags] [user]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
rc = vm->unshareVolume(argv[2], argv[3]);
} else if (!strcmp(argv[1], "shared")) {
bool enabled = false;
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: volume shared <path> <method>", false);
return 0;
int flags = (argc > 3) ? atoi(argv[3]) : 0;
userid_t user = (argc > 4) ? atoi(argv[4]) : -1;
if (flags & android::vold::VolumeBase::Flags::kPrimary) {
vm->setPrimary(vol);
}
if (vm->shareEnabled(argv[2], argv[3], &enabled)) {
cli->sendMsg(
ResponseCode::OperationFailed, "Failed to determine share enable state", true);
} else {
cli->sendMsg(ResponseCode::ShareEnabledResult,
(enabled ? "Share enabled" : "Share disabled"), false);
vol->setFlags(flags);
vol->setUser(user);
return sendGenericOkFail(cli, vol->mount());
} else if (cmd == "unmount" && argc > 2) {
// unmount [volId]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
return 0;
} else if (!strcmp(argv[1], "mkdirs")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mkdirs <path>", false);
return 0;
return sendGenericOkFail(cli, vol->unmount());
} else if (cmd == "format" && argc > 2) {
// format [volId]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
rc = vm->mkdirs(argv[2]);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);
}
if (!rc) {
cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);
} else {
rc = ResponseCode::convertFromErrno();
cli->sendMsg(rc, "volume operation failed", true);
return sendGenericOkFail(cli, vol->format());
}
return 0;
return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
CommandListener::StorageCmd::StorageCmd() :
@ -616,7 +619,7 @@ int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
if (rc == 0) {
break;
} else if (tries == 0) {
Process::killProcessesWithOpenFiles(DATA_MNT_POINT, 2);
Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
}
}
} else if (!strcmp(argv[1], "changepw")) {

@ -18,6 +18,7 @@
#define _COMMANDLISTENER_H__
#include <sysutils/FrameworkListener.h>
#include <utils/Errors.h>
#include "VoldCommand.h"
class CommandListener : public FrameworkListener {
@ -27,6 +28,7 @@ public:
private:
static void dumpArgs(int argc, char **argv, int argObscure);
static int sendGenericOkFail(SocketClient *cli, int cond);
class DumpCmd : public VoldCommand {
public:

@ -14,16 +14,16 @@
* limitations under the License.
*/
#define LOG_TAG "Vold"
#include "Disk.h"
#include "PublicVolume.h"
#include "Utils.h"
#include "VolumeBase.h"
#include "VolumeManager.h"
#include "ResponseCode.h"
#include <base/file.h>
#include <base/stringprintf.h>
#include <cutils/log.h>
#include <base/logging.h>
#include <diskconfig/diskconfig.h>
#include <fcntl.h>
@ -57,40 +57,84 @@ enum class Table {
kGpt,
};
Disk::Disk(const std::string& eventPath, dev_t device) :
mDevice(device), mSize(-1) {
mId = StringPrintf("disk:%ud:%ud", major(device), minor(device));
Disk::Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags) :
mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(false) {
mId = StringPrintf("disk:%u,%u", major(device), minor(device));
mEventPath = eventPath;
mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
mDevPath = StringPrintf("/dev/block/vold/%ud:%ud", major(device), minor(device));
mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
CreateDeviceNode(mDevPath, mDevice);
}
Disk::~Disk() {
CHECK(!mCreated);
DestroyDeviceNode(mDevPath);
}
std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
for (std::shared_ptr<VolumeBase>& v : mParts) {
if (!id.compare(v->getId())) {
return v;
for (auto vol : mVolumes) {
if (vol->getId() == id) {
return vol;
}
auto stackedVol = vol->findVolume(id);
if (stackedVol != nullptr) {
return stackedVol;
}
}
return nullptr;
}
status_t Disk::create() {
CHECK(!mCreated);
mCreated = true;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::DiskCreated,
StringPrintf("%s %d", getId().c_str(), mFlags).c_str(), false);
readMetadata();
readPartitions();
return OK;
}
status_t Disk::destroy() {
CHECK(mCreated);
destroyAllVolumes();
mCreated = false;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::DiskDestroyed, getId().c_str(), false);
return OK;
}
void Disk::createPublicVolume(dev_t device) {
auto vol = new PublicVolume(device);
vol->create();
mVolumes.push_back(std::shared_ptr<VolumeBase>(vol));
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::DiskVolumeCreated,
StringPrintf("%s %s", getId().c_str(), vol->getId().c_str()).c_str(), false);
}
void Disk::createPrivateVolume(dev_t device) {
// TODO: create and add
}
void Disk::destroyAllVolumes() {
for (auto vol : mVolumes) {
vol->destroy();
}
mVolumes.clear();
}
status_t Disk::readMetadata() {
mSize = -1;
mLabel = "";
mLabel.clear();
{
std::string path(mSysPath + "/size");
std::string tmp;
if (!ReadFileToString(path, &tmp)) {
ALOGW("Failed to read size from %s: %s", path.c_str(), strerror(errno));
return -errno;
int fd = open(mDevPath.c_str(), O_RDONLY);
if (fd != -1) {
if (ioctl(fd, BLKGETSIZE64, &mSize)) {
mSize = -1;
}
mSize = strtoll(tmp.c_str(), nullptr, 10);
close(fd);
}
switch (major(mDevice)) {
@ -98,7 +142,7 @@ status_t Disk::readMetadata() {
std::string path(mSysPath + "/device/vendor");
std::string tmp;
if (!ReadFileToString(path, &tmp)) {
ALOGW("Failed to read vendor from %s: %s", path.c_str(), strerror(errno));
PLOG(WARNING) << "Failed to read vendor from " << path;
return -errno;
}
mLabel = tmp;
@ -108,7 +152,7 @@ status_t Disk::readMetadata() {
std::string path(mSysPath + "/device/manfid");
std::string tmp;
if (!ReadFileToString(path, &tmp)) {
ALOGW("Failed to read manufacturer from %s: %s", path.c_str(), strerror(errno));
PLOG(WARNING) << "Failed to read manufacturer from " << path;
return -errno;
}
uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
@ -124,11 +168,18 @@ status_t Disk::readMetadata() {
break;
}
default: {
ALOGW("Unsupported block major type %d", major(mDevice));
LOG(WARNING) << "Unsupported block major type" << major(mDevice);
return -ENOTSUP;
}
}
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::DiskSizeChanged,
StringPrintf("%s %lld", getId().c_str(), mSize).c_str(), false);
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::DiskLabelChanged,
StringPrintf("%s %s", getId().c_str(), mLabel.c_str()).c_str(), false);
return OK;
}
@ -138,7 +189,7 @@ status_t Disk::readPartitions() {
return -ENOTSUP;
}
mParts.clear();
destroyAllVolumes();
// Parse partition table
std::string path(kSgdiskPath);
@ -146,68 +197,64 @@ status_t Disk::readPartitions() {
path += mDevPath;
FILE* fp = popen(path.c_str(), "r");
if (!fp) {
ALOGE("Failed to run %s: %s", path.c_str(), strerror(errno));
PLOG(ERROR) << "Failed to run " << path;
return -errno;
}
char line[1024];
Table table = Table::kUnknown;
bool foundParts = false;
while (fgets(line, sizeof(line), fp) != nullptr) {
LOG(DEBUG) << "sgdisk: " << line;
char* token = strtok(line, kSgdiskToken);
if (token == nullptr) continue;
if (!strcmp(token, "DISK")) {
const char* type = strtok(nullptr, kSgdiskToken);
ALOGD("%s: found %s partition table", mId.c_str(), type);
if (!strcmp(type, "mbr")) {
table = Table::kMbr;
} else if (!strcmp(type, "gpt")) {
table = Table::kGpt;
}
} else if (!strcmp(token, "PART")) {
foundParts = true;
int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
if (i <= 0 || i > maxMinors) {
ALOGW("%s: ignoring partition %d beyond max supported devices",
mId.c_str(), i);
LOG(WARNING) << mId << " is ignoring partition " << i
<< " beyond max supported devices";
continue;
}
dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
VolumeBase* vol = nullptr;
if (table == Table::kMbr) {
const char* type = strtok(nullptr, kSgdiskToken);
ALOGD("%s: MBR partition %d type %s", mId.c_str(), i, type);
switch (strtol(type, nullptr, 16)) {
case 0x06: // FAT16
case 0x0b: // W95 FAT32 (LBA)
case 0x0c: // W95 FAT32 (LBA)
case 0x0e: // W95 FAT16 (LBA)
vol = new PublicVolume(partDevice);
createPublicVolume(partDevice);
break;
}
} else if (table == Table::kGpt) {
const char* typeGuid = strtok(nullptr, kSgdiskToken);
const char* partGuid = strtok(nullptr, kSgdiskToken);
ALOGD("%s: GPT partition %d type %s, GUID %s", mId.c_str(), i,
typeGuid, partGuid);
if (!strcasecmp(typeGuid, kGptBasicData)) {
vol = new PublicVolume(partDevice);
createPublicVolume(partDevice);
} else if (!strcasecmp(typeGuid, kGptAndroidExt)) {
//vol = new PrivateVolume();
createPrivateVolume(partDevice);
}
}
if (vol != nullptr) {
mParts.push_back(std::shared_ptr<VolumeBase>(vol));
}
}
}
// Ugly last ditch effort, treat entire disk as partition
if (table == Table::kUnknown) {
ALOGD("%s: unknown partition table; trying entire device", mId.c_str());
VolumeBase* vol = new PublicVolume(mDevice);
mParts.push_back(std::shared_ptr<VolumeBase>(vol));
if (table == Table::kUnknown || !foundParts) {
LOG(WARNING) << mId << " has unknown partition table; trying entire device";
createPublicVolume(mDevice);
}
pclose(fp);
@ -216,13 +263,13 @@ status_t Disk::readPartitions() {
status_t Disk::partitionPublic() {
// TODO: improve this code
destroyAllVolumes();
struct disk_info dinfo;
memset(&dinfo, 0, sizeof(dinfo));
if (!(dinfo.part_lst = (struct part_info *) malloc(
MAX_NUM_PARTS * sizeof(struct part_info)))) {
SLOGE("Failed to malloc prt_lst");
return -1;
}
@ -243,7 +290,7 @@ status_t Disk::partitionPublic() {
int rc = apply_disk_config(&dinfo, 0);
if (rc) {
SLOGE("Failed to apply disk configuration (%d)", rc);
LOG(ERROR) << "Failed to apply disk configuration: " << rc;
goto out;
}
@ -256,10 +303,12 @@ out:
}
status_t Disk::partitionPrivate() {
destroyAllVolumes();
return -ENOTSUP;
}
status_t Disk::partitionMixed(int8_t ratio) {
destroyAllVolumes();
return -ENOTSUP;
}
@ -274,14 +323,14 @@ int Disk::getMaxMinors() {
// Per Documentation/devices.txt this is dynamic
std::string tmp;
if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp)) {
ALOGW("Failed to read max minors");
LOG(ERROR) << "Failed to read max minors";
return -errno;
}
return atoi(tmp.c_str());
}
}
ALOGW("Unsupported block major type %d", major(mDevice));
LOG(ERROR) << "Unsupported block major type " << major(mDevice);
return -ENOTSUP;
}

@ -28,16 +28,6 @@ namespace vold {
class VolumeBase;
// events:
// disk_created 127:4
// disk_meta 127:4 [size] [label]
// disk_destroyed 127:4
// commands:
// disk partition_public 127:4
// disk partition_private 127:4
// disk partition_mixed 127:4 50
/*
* Representation of detected physical media.
*
@ -46,18 +36,35 @@ class VolumeBase;
*/
class Disk {
public:
Disk(const std::string& eventPath, dev_t device);
Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags);
virtual ~Disk();
enum Flags {
/* Flag that disk is adoptable */
kAdoptable = 1 << 0,
/* Flag that disk is considered primary when the user hasn't
* explicitly picked a primary storage location */
kDefaultPrimary = 1 << 1,
/* Flag that disk is SD card */
kSd = 1 << 2,
/* Flag that disk is USB disk */
kUsb = 1 << 3,
};
const std::string& getId() { return mId; }
const std::string& getEventPath() { return mEventPath; }
const std::string& getSysPath() { return mSysPath; }
const std::string& getDevPath() { return mDevPath; }
dev_t getDevice() { return mDevice; }
uint64_t getSize() { return mSize; }
const std::string& getLabel() { return mLabel; }
int getFlags() { return mFlags; }
std::shared_ptr<VolumeBase> findVolume(const std::string& id);
status_t create();
status_t destroy();
status_t readMetadata();
status_t readPartitions();
@ -68,6 +75,8 @@ public:
private:
/* ID that uniquely references this disk */
std::string mId;
/* Original event path */
std::string mEventPath;
/* Device path under sysfs */
std::string mSysPath;
/* Device path under dev */
@ -79,7 +88,18 @@ private:
/* User-visible label, such as manufacturer */
std::string mLabel;
/* Current partitions on disk */
std::vector<std::shared_ptr<VolumeBase>> mParts;
std::vector<std::shared_ptr<VolumeBase>> mVolumes;
/* Nickname for this disk */
std::string mNickname;
/* Flags applicable to this disk */
int mFlags;
/* Flag indicating object is created */
bool mCreated;
void createPublicVolume(dev_t device);
void createPrivateVolume(dev_t device);
void destroyAllVolumes();
int getMaxMinors();

@ -14,14 +14,12 @@
* limitations under the License.
*/
#define LOG_TAG "Vold"
#include "EmulatedVolume.h"
#include "Utils.h"
#include <base/stringprintf.h>
#include <base/logging.h>
#include <cutils/fs.h>
#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <fcntl.h>
@ -38,37 +36,46 @@ namespace vold {
static const char* kFusePath = "/system/bin/sdcard";
static const char* kUserMountPath = "/mnt/user";
EmulatedVolume::EmulatedVolume(const std::string& rawPath,
const std::string& fsUuid) : VolumeBase(Type::kEmulated), mFusePid(0) {
if (fsUuid.empty()) {
setId("emulated");
} else {
setId(StringPrintf("emulated:%s", fsUuid.c_str()));
}
EmulatedVolume::EmulatedVolume(const std::string& rawPath, const std::string& nickname) :
VolumeBase(VolumeType::kEmulated), mFusePid(0), mPrimary(false) {
mRawPath = rawPath;
mFusePath = StringPrintf("/mnt/media_rw/emulated_fuse_%s", nickname.c_str());
mFusePath = StringPrintf("/storage/%s", getId().c_str());
}
EmulatedVolume::~EmulatedVolume() {
}
status_t EmulatedVolume::doMount() {
if (fs_prepare_dir(mFusePath.c_str(), 0770, AID_MEDIA_RW, AID_MEDIA_RW)) {
SLOGE("Failed to create mount point %s: %s", mFusePath.c_str(), strerror(errno));
if (fs_prepare_dir(mFusePath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << "Failed to create mount point " << mFusePath;
return -errno;
}
setPath(mFusePath);
if (!(mFusePid = fork())) {
if (execl(kFusePath,
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-d",
"-l",
mRawPath.c_str(),
mFusePath.c_str())) {
SLOGE("Failed to exec: %s", strerror(errno));
mFusePath.c_str(),
NULL)) {
PLOG(ERROR) << "Failed to exec";
}
PLOG(DEBUG) << "FUSE exiting";
_exit(1);
}
if (mFusePid == -1) {
SLOGE("Failed to fork: %s", strerror(errno));
PLOG(ERROR) << "Failed to fork";
return -errno;
}
@ -83,50 +90,15 @@ status_t EmulatedVolume::doUnmount() {
}
ForceUnmount(mFusePath);
ForceUnmount(mRawPath);
TEMP_FAILURE_RETRY(unlink(mFusePath.c_str()));
return OK;
}
status_t EmulatedVolume::doFormat() {
return -ENOTSUP;
}
status_t EmulatedVolume::bindUser(userid_t user) {
return bindUserInternal(user, true);
}
status_t EmulatedVolume::unbindUser(userid_t user) {
return bindUserInternal(user, false);
}
status_t EmulatedVolume::bindUserInternal(userid_t user, bool bind) {
if (!mPrimary) {
// Emulated volumes are only bound when primary
return OK;
}
std::string fromPath(StringPrintf("%s/%ud", mFusePath.c_str(), user));
std::string toPath(StringPrintf("%s/%ud/primary", kUserMountPath, user));
if (bind) {
mountBind(fromPath, toPath);
} else {
unmountBind(toPath);
if (TEMP_FAILURE_RETRY(rmdir(mFusePath.c_str()))) {
PLOG(ERROR) << "Failed to rmdir mount point " << mFusePath;
return -errno;
}
return OK;
}
void EmulatedVolume::setPrimary(bool primary) {
if (getState() != VolumeState::kUnmounted) {
SLOGE("Primary state change requires %s to be unmounted", getId().c_str());
return;
}
mPrimary = primary;
}
} // namespace vold
} // namespace android

@ -37,31 +37,20 @@ namespace vold {
*/
class EmulatedVolume : public VolumeBase {
public:
EmulatedVolume(const std::string& rawPath, const std::string& nickname);
EmulatedVolume(const std::string& rawPath, const std::string& fsUuid);
virtual ~EmulatedVolume();
void setPrimary(bool primary);
bool getPrimary() { return mPrimary; }
status_t bindUser(userid_t user);
status_t unbindUser(userid_t user);
protected:
status_t doMount() override;
status_t doUnmount() override;
private:
/* Mount point of raw storage */
std::string mRawPath;
/* Mount point of FUSE wrapper */
/* Mount point of visible storage */
std::string mFusePath;
/* PID of FUSE wrapper */
pid_t mFusePid;
/* Flag indicating this is primary storage */
bool mPrimary;
protected:
status_t doMount();
status_t doUnmount();
status_t doFormat();
status_t bindUserInternal(userid_t user, bool bind);
DISALLOW_COPY_AND_ASSIGN(EmulatedVolume);
};

@ -58,8 +58,8 @@ int NetlinkManager::start() {
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;
if ((mSock = socket(PF_NETLINK,
SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}

@ -170,18 +170,14 @@ int Process::getPid(const char *s) {
return result;
}
extern "C" void vold_killProcessesWithOpenFiles(const char *path, int action) {
Process::killProcessesWithOpenFiles(path, action);
extern "C" void vold_killProcessesWithOpenFiles(const char *path, int signal) {
Process::killProcessesWithOpenFiles(path, signal);
}
/*
* Hunt down processes that have files open at the given mount point.
* action = 0 to just warn,
* action = 1 to SIGHUP,
* action = 2 to SIGKILL
*/
// hunt down and kill processes that have files open on the given mount point
void Process::killProcessesWithOpenFiles(const char *path, int action) {
void Process::killProcessesWithOpenFiles(const char *path, int signal) {
DIR* dir;
struct dirent* de;
@ -213,12 +209,10 @@ void Process::killProcessesWithOpenFiles(const char *path, int action) {
} else {
continue;
}
if (action == 1) {
SLOGW("Sending SIGHUP to process %d", pid);
kill(pid, SIGTERM);
} else if (action == 2) {
SLOGE("Sending SIGKILL to process %d", pid);
kill(pid, SIGKILL);
if (signal != 0) {
SLOGW("Sending %s to process %d", strsignal(signal), pid);
kill(pid, signal);
}
}
closedir(dir);

@ -21,7 +21,7 @@
class Process {
public:
static void killProcessesWithOpenFiles(const char *path, int action);
static void killProcessesWithOpenFiles(const char *path, int signal);
static int getPid(const char *s);
static int checkSymLink(int pid, const char *path, const char *name);
static int checkFileMaps(int pid, const char *path);
@ -36,7 +36,7 @@ private:
extern "C" {
#endif /* __cplusplus */
void vold_killProcessesWithOpenFiles(const char *path, int action);
void vold_killProcessesWithOpenFiles(const char *path, int signal);
#ifdef __cplusplus
}
#endif

@ -14,15 +14,15 @@
* limitations under the License.
*/
#define LOG_TAG "Vold"
#include "Fat.h"
#include "PublicVolume.h"
#include "Utils.h"
#include "VolumeManager.h"
#include "ResponseCode.h"
#include <base/stringprintf.h>
#include <base/logging.h>
#include <cutils/fs.h>
#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <fcntl.h>
@ -40,16 +40,13 @@ namespace vold {
static const char* kBlkidPath = "/system/bin/blkid";
static const char* kFusePath = "/system/bin/sdcard";
static const char* kUserMountPath = "/mnt/user";
static const char* kAsecPath = "/mnt/secure/asec";
PublicVolume::PublicVolume(dev_t device) :
VolumeBase(VolumeType::kPublic), mDevice(device), mFusePid(0), mPrimary(false) {
mId = StringPrintf("public:%ud:%ud", major(device), minor(device));
mDevPath = StringPrintf("/dev/block/vold/%ud:%ud", major(device), minor(device));
mRawPath = StringPrintf("/mnt/media_rw/public_raw_%ud:%ud", major(device), minor(device));
mFusePath = StringPrintf("/mnt/media_rw/public_fuse_%ud:%ud", major(device), minor(device));
CreateDeviceNode(mDevPath, device);
VolumeBase(Type::kPublic), mDevice(device), mFusePid(0) {
setId(StringPrintf("public:%u,%u", major(device), minor(device)));
mDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
CreateDeviceNode(mDevPath, mDevice);
}
PublicVolume::~PublicVolume() {
@ -57,22 +54,29 @@ PublicVolume::~PublicVolume() {
}
status_t PublicVolume::readMetadata() {
mFsUuid = "";
mFsLabel = "";
mFsType.clear();
mFsUuid.clear();
mFsLabel.clear();
std::string path(StringPrintf("%s -c /dev/null %s", kBlkidPath, mDevPath.c_str()));
FILE* fp = popen(path.c_str(), "r");
if (!fp) {
ALOGE("Failed to run %s: %s", path.c_str(), strerror(errno));
PLOG(ERROR) << "Failed to run " << path;
return -errno;
}
status_t res = OK;
char line[1024];
char value[128];
if (fgets(line, sizeof(line), fp) != nullptr) {
ALOGD("blkid identified as %s", line);
LOG(DEBUG) << "blkid identified as " << line;
char* start = strstr(line, "TYPE=");
if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
mFsType = value;
}
char* start = strstr(line, "UUID=");
start = strstr(line, "UUID=");
if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
mFsUuid = value;
}
@ -82,14 +86,23 @@ status_t PublicVolume::readMetadata() {
mFsLabel = value;
}
} else {
ALOGW("blkid failed to identify %s", mDevPath.c_str());
return -ENODATA;
LOG(WARNING) << "blkid failed to identify " << mDevPath;
res = -ENODATA;
}
pclose(fp);
// TODO: broadcast ident to framework
return OK;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumeFsTypeChanged,
StringPrintf("%s %s", getId().c_str(), mFsType.c_str()).c_str(), false);
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumeFsUuidChanged,
StringPrintf("%s %s", getId().c_str(), mFsUuid.c_str()).c_str(), false);
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumeFsLabelChanged,
StringPrintf("%s %s", getId().c_str(), mFsLabel.c_str()).c_str(), false);
return res;
}
status_t PublicVolume::initAsecStage() {
@ -100,66 +113,96 @@ status_t PublicVolume::initAsecStage() {
if (!access(legacyPath.c_str(), R_OK | X_OK)
&& access(securePath.c_str(), R_OK | X_OK)) {
if (rename(legacyPath.c_str(), securePath.c_str())) {
SLOGE("Failed to rename legacy ASEC dir: %s", strerror(errno));
PLOG(WARNING) << "Failed to rename legacy ASEC dir";
}
}
if (fs_prepare_dir(securePath.c_str(), 0770, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
SLOGW("fs_prepare_dir failed: %s", strerror(errno));
return -errno;
if (TEMP_FAILURE_RETRY(mkdir(securePath.c_str(), 0700))) {
if (errno != EEXIST) {
PLOG(WARNING) << "Creating ASEC stage failed";
return -errno;
}
}
BindMount(securePath, kAsecPath);
return OK;
}
status_t PublicVolume::doMount() {
// TODO: expand to support mounting other filesystems
if (Fat::check(mDevPath.c_str())) {
SLOGE("Failed filesystem check; not mounting");
LOG(ERROR) << "Failed filesystem check; not mounting";
return -EIO;
}
if (fs_prepare_dir(mRawPath.c_str(), 0770, AID_MEDIA_RW, AID_MEDIA_RW)) {
SLOGE("Failed to create mount point %s: %s", mRawPath.c_str(), strerror(errno));
readMetadata();
// Use UUID as stable name, if available
std::string stableName = getId();
if (!mFsUuid.empty()) {
stableName = "public:" + mFsUuid;
}
mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
mFusePath = StringPrintf("/storage/%s", stableName.c_str());
setPath(mFusePath);
if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << "Failed to create mount point " << mRawPath;
return -errno;
}
if (fs_prepare_dir(mFusePath.c_str(), 0770, AID_MEDIA_RW, AID_MEDIA_RW)) {
SLOGE("Failed to create mount point %s: %s", mFusePath.c_str(), strerror(errno));
if (fs_prepare_dir(mFusePath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << "Failed to create mount point " << mFusePath;
return -errno;
}
if (Fat::doMount(mDevPath.c_str(), mRawPath.c_str(), false, false, false,
AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
SLOGE("Failed to mount %s: %s", mDevPath.c_str(), strerror(errno));
PLOG(ERROR) << "Failed to mount " << mDevPath;
return -EIO;
}
if (getFlags() & Flags::kPrimary) {
initAsecStage();
}
// Only need to spin up FUSE when visible
if (!(getFlags() & Flags::kVisible)) {
return OK;
}
// TODO: teach FUSE daemon to protect itself with user-specific GID
if (!(mFusePid = fork())) {
if (mPrimary) {
if (execl(kFusePath,
if (getFlags() & Flags::kPrimary) {
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-d",
mRawPath.c_str(),
mFusePath.c_str())) {
SLOGE("Failed to exec: %s", strerror(errno));
mFusePath.c_str(),
NULL)) {
PLOG(ERROR) << "Failed to exec";
}
} else {
if (execl(kFusePath,
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-w", "1023", // AID_MEDIA_RW
"-d",
mRawPath.c_str(),
mFusePath.c_str())) {
SLOGE("Failed to exec: %s", strerror(errno));
mFusePath.c_str(),
NULL)) {
PLOG(ERROR) << "Failed to exec";
}
}
PLOG(DEBUG) << "FUSE exiting";
_exit(1);
}
if (mFusePid == -1) {
SLOGE("Failed to fork: %s", strerror(errno));
PLOG(ERROR) << "Failed to fork";
return -errno;
}
@ -176,77 +219,26 @@ status_t PublicVolume::doUnmount() {
ForceUnmount(mFusePath);
ForceUnmount(mRawPath);
TEMP_FAILURE_RETRY(unlink(mRawPath.c_str()));
TEMP_FAILURE_RETRY(unlink(mFusePath.c_str()));
if (TEMP_FAILURE_RETRY(rmdir(mRawPath.c_str()))) {
PLOG(ERROR) << "Failed to rmdir mount point " << mRawPath;
}
if (TEMP_FAILURE_RETRY(rmdir(mFusePath.c_str()))) {
PLOG(ERROR) << "Failed to rmdir mount point " << mFusePath;
}
mFusePath.clear();
mRawPath.clear();
return OK;
}
status_t PublicVolume::doFormat() {
if (Fat::format(mDevPath.c_str(), 0, true)) {
SLOGE("Failed to format: %s", strerror(errno));
LOG(ERROR) << "Failed to format";
return -errno;
}
return OK;
}
status_t PublicVolume::bindUser(userid_t user) {
return bindUserInternal(user, true);
}
status_t PublicVolume::unbindUser(userid_t user) {
return bindUserInternal(user, false);
}
status_t PublicVolume::bindUserInternal(userid_t user, bool bind) {
if (mPrimary) {
if (user == 0) {
std::string path(StringPrintf("%s/%ud/primary", kUserMountPath, user));
if (bind) {
mountBind(mFusePath, path);
} else {
unmountBind(path);
}
} else {
// Public volumes are only visible to owner when primary
// storage, so we don't mount for secondary users.
}
} else {
std::string path(StringPrintf("%s/%ud/public_%ud:%ud", kUserMountPath, user,
major(mDevice), minor(mDevice)));
if (bind) {
mountBind(mFusePath, path);
} else {
unmountBind(path);
}
if (user != 0) {
// To prevent information leakage between users, only owner
// has access to the Android directory
path += "/Android";
if (bind) {
if (::mount("tmpfs", path.c_str(), "tmpfs", MS_NOSUID, "mode=0000")) {
SLOGE("Failed to protect secondary path %s: %s",
path.c_str(), strerror(errno));
return -errno;
}
} else {
ForceUnmount(path);
}
}
}
return OK;
}
void PublicVolume::setPrimary(bool primary) {
if (getState() != VolumeState::kUnmounted) {
SLOGE("Primary state change requires %s to be unmounted", getId().c_str());
return;
}
mPrimary = primary;
}
} // namespace vold
} // namespace android

@ -42,24 +42,13 @@ public:
explicit PublicVolume(dev_t device);
virtual ~PublicVolume();
status_t readMetadata();
status_t initAsecStage();
void setPrimary(bool primary);
bool getPrimary() { return mPrimary; }
const std::string& getFsUuid() { return mFsUuid; }
const std::string& getFsLabel() { return mFsLabel; }
status_t bindUser(userid_t user);
status_t unbindUser(userid_t user);
protected:
status_t doMount();
status_t doUnmount();
status_t doFormat();
status_t doMount() override;
status_t doUnmount() override;
status_t doFormat() override;
status_t bindUserInternal(userid_t user, bool bind);
status_t readMetadata();
status_t initAsecStage();
private:
/* Kernel device representing partition */
@ -72,12 +61,12 @@ private:
std::string mFusePath;
/* PID of FUSE wrapper */
pid_t mFusePid;
/* Flag indicating this is primary storage */
bool mPrimary;
/* Parsed UUID from filesystem */
/* Filesystem type */
std::string mFsType;
/* Filesystem UUID */
std::string mFsUuid;
/* User-visible label from filesystem */
/* User-visible filesystem label */
std::string mFsLabel;
DISALLOW_COPY_AND_ASSIGN(PublicVolume);

@ -66,6 +66,20 @@ public:
static const int VolumeDiskRemoved = 631;
static const int VolumeBadRemoval = 632;
static const int DiskCreated = 640;
static const int DiskSizeChanged = 641;
static const int DiskLabelChanged = 642;
static const int DiskVolumeCreated = 643;
static const int DiskDestroyed = 649;
static const int VolumeCreated = 650;
static const int VolumeStateChanged = 651;
static const int VolumeFsTypeChanged = 652;
static const int VolumeFsUuidChanged = 653;
static const int VolumeFsLabelChanged = 654;
static const int VolumePathChanged = 655;
static const int VolumeDestroyed = 659;
static int convertFromErrno();
};
#endif

@ -14,14 +14,12 @@
* limitations under the License.
*/
#define LOG_TAG "Vold"
#include "sehandle.h"
#include "Utils.h"
#include "Process.h"
#include <base/logging.h>
#include <cutils/fs.h>
#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <fcntl.h>
@ -53,8 +51,8 @@ status_t CreateDeviceNode(const std::string& path, dev_t dev) {
mode_t mode = 0660 | S_IFBLK;
if (mknod(cpath, mode, dev) < 0) {
if (errno != EEXIST) {
ALOGW("Failed to create device node for %ud:%ud at %s: %s",
major(dev), minor(dev), cpath, strerror(errno));
PLOG(ERROR) << "Failed to create device node for " << major(dev)
<< ":" << minor(dev) << " at " << path;
res = -errno;
}
}
@ -81,23 +79,31 @@ status_t ForceUnmount(const std::string& path) {
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
return OK;
}
ALOGW("Failed to unmount %s (%s), sending SIGTERM", cpath, strerror(errno));
PLOG(WARNING) << "Failed to unmount " << path << "; sending SIGTERM";
Process::killProcessesWithOpenFiles(cpath, SIGTERM);
sleep(1);
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
return OK;
}
ALOGW("Failed to unmount %s (%s), sending SIGKILL", cpath, strerror(errno));
PLOG(WARNING) << "Failed to unmount " << path << "; sending SIGKILL";
Process::killProcessesWithOpenFiles(cpath, SIGKILL);
sleep(1);
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
return OK;
}
ALOGW("Failed to unmount %s (%s)", cpath, strerror(errno));
PLOG(ERROR) << "Failed to unmount " << path << "; giving up";
return -errno;
}
status_t BindMount(const std::string& source, const std::string& target) {
if (::mount(source.c_str(), target.c_str(), "", MS_BIND, NULL)) {
PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
return -errno;
}
return OK;
}
} // namespace vold
} // namespace android

@ -38,6 +38,9 @@ status_t DestroyDeviceNode(const std::string& path);
/* Really unmounts the path, killing active processes along the way */
status_t ForceUnmount(const std::string& path);
/* Creates bind mount from source to target */
status_t BindMount(const std::string& source, const std::string& target);
} // namespace vold
} // namespace android

@ -521,20 +521,20 @@ int Volume::doUnmount(const char *path, bool force) {
return 0;
}
int action = 0;
int signal = 0;
if (force) {
if (retries == 1) {
action = 2; // SIGKILL
signal = SIGKILL;
} else if (retries == 2) {
action = 1; // SIGHUP
signal = SIGTERM;
}
}
SLOGW("Failed to unmount %s (%s, retries %d, action %d)",
path, strerror(errno), retries, action);
SLOGW("Failed to unmount %s (%s, retries %d, signal %d)",
path, strerror(errno), retries, signal);
Process::killProcessesWithOpenFiles(path, action);
Process::killProcessesWithOpenFiles(path, signal);
usleep(1000*1000);
}
errno = EBUSY;

@ -14,12 +14,13 @@
* limitations under the License.
*/
#define LOG_TAG "Vold"
#include "Utils.h"
#include "VolumeBase.h"
#include "VolumeManager.h"
#include "ResponseCode.h"
#include <cutils/log.h>
#include <base/stringprintf.h>
#include <base/logging.h>
#include <fcntl.h>
#include <stdlib.h>
@ -27,83 +28,158 @@
#include <sys/stat.h>
#include <sys/types.h>
using android::base::StringPrintf;
#define DEBUG 1
namespace android {
namespace vold {
VolumeBase::VolumeBase(VolumeType type) :
mType(type), mState(VolumeState::kUnmounted) {
VolumeBase::VolumeBase(Type type) :
mType(type), mFlags(0), mUser(-1), mCreated(false), mState(State::kUnmounted) {
}
VolumeBase::~VolumeBase() {
CHECK(!mCreated);
}
void VolumeBase::setState(VolumeState state) {
void VolumeBase::setState(State state) {
mState = state;
// TODO: publish state up to framework
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumeStateChanged,
StringPrintf("%s %d", getId().c_str(), mState).c_str(), false);
}
void VolumeBase::stackVolume(const std::shared_ptr<VolumeBase>& volume) {
mStacked.push_back(volume);
status_t VolumeBase::setFlags(int flags) {
if (mState != State::kUnmounted) {
LOG(WARNING) << getId() << " flags change requires state unmounted";
return -EBUSY;
}
mFlags = flags;
return OK;
}
status_t VolumeBase::setUser(userid_t user) {
if (mState != State::kUnmounted) {
LOG(WARNING) << getId() << " user change requires state unmounted";
return -EBUSY;
}
mUser = user;
return OK;
}
status_t VolumeBase::setId(const std::string& id) {
if (mCreated) {
LOG(WARNING) << getId() << " id change requires not created";
return -EBUSY;
}
mId = id;
return OK;
}
status_t VolumeBase::setPath(const std::string& path) {
if (mState != State::kMounting) {
LOG(WARNING) << getId() << " path change requires state mounting";
return -EBUSY;
}
mPath = path;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumePathChanged,
StringPrintf("%s %s", getId().c_str(), mPath.c_str()).c_str(), false);
return OK;
}
void VolumeBase::unstackVolume(const std::shared_ptr<VolumeBase>& volume) {
mStacked.remove(volume);
void VolumeBase::addVolume(const std::shared_ptr<VolumeBase>& volume) {
mVolumes.push_back(volume);
}
void VolumeBase::removeVolume(const std::shared_ptr<VolumeBase>& volume) {
mVolumes.remove(volume);
}
std::shared_ptr<VolumeBase> VolumeBase::findVolume(const std::string& id) {
for (auto vol : mVolumes) {
if (vol->getId() == id) {
return vol;
}
}
return nullptr;
}
status_t VolumeBase::create() {
CHECK(!mCreated);
mCreated = true;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumeCreated,
StringPrintf("%s %d", getId().c_str(), mType).c_str(), false);
return OK;
}
status_t VolumeBase::destroy() {
CHECK(mCreated);
if (mState == State::kMounted) {
unmount();
}
mCreated = false;
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
ResponseCode::VolumeDestroyed, getId().c_str(), false);
return OK;
}
status_t VolumeBase::mount() {
if (getState() != VolumeState::kUnmounted) {
SLOGE("Must be unmounted to mount %s", getId().c_str());
if (mState != State::kUnmounted) {
LOG(WARNING) << getId() << " mount requires state unmounted";
return -EBUSY;
}
setState(VolumeState::kMounting);
setState(State::kMounting);
status_t res = doMount();
if (!res) {
setState(VolumeState::kMounted);
if (res == OK) {
setState(State::kMounted);
} else {
setState(VolumeState::kCorrupt);
setState(State::kUnmounted);
}
return res;
}
status_t VolumeBase::unmount() {
if (getState() != VolumeState::kMounted) {
SLOGE("Must be mounted to unmount %s", getId().c_str());
if (mState != State::kMounted) {
LOG(WARNING) << getId() << " unmount requires state mounted";
return -EBUSY;
}
setState(VolumeState::kUnmounting);
setState(State::kUnmounting);
for (std::string target : mBindTargets) {
ForceUnmount(target);
}
mBindTargets.clear();
for (std::shared_ptr<VolumeBase> v : mStacked) {
if (v->unmount()) {
ALOGW("Failed to unmount %s stacked above %s", v->getId().c_str(),
getId().c_str());
for (auto vol : mVolumes) {
if (vol->unmount()) {
LOG(WARNING) << getId() << " failed to unmount " << vol->getId()
<< " stacked above";
}
}
mStacked.clear();
mVolumes.clear();
status_t res = doUnmount();
setState(VolumeState::kUnmounted);
setState(State::kUnmounted);
return res;
}
status_t VolumeBase::format() {
if (getState() != VolumeState::kUnmounted
|| getState() != VolumeState::kCorrupt) {
SLOGE("Must be unmounted or corrupt to format %s", getId().c_str());
if (mState != State::kUnmounted) {
LOG(WARNING) << getId() << " format requires state unmounted";
return -EBUSY;
}
setState(VolumeState::kFormatting);
setState(State::kFormatting);
status_t res = doFormat();
setState(VolumeState::kUnmounted);
setState(State::kUnmounted);
return res;
}
@ -111,21 +187,5 @@ status_t VolumeBase::doFormat() {
return -ENOTSUP;
}
status_t VolumeBase::mountBind(const std::string& source, const std::string& target) {
if (::mount(source.c_str(), target.c_str(), "", MS_BIND, NULL)) {
SLOGE("Failed to bind mount %s to %s: %s", source.c_str(),
target.c_str(), strerror(errno));
return -errno;
}
mBindTargets.push_back(target);
return OK;
}
status_t VolumeBase::unmountBind(const std::string& target) {
ForceUnmount(target);
mBindTargets.remove(target);
return OK;
}
} // namespace vold
} // namespace android

@ -19,6 +19,7 @@
#include "Utils.h"
#include <cutils/multiuser.h>
#include <utils/Errors.h>
#include <sys/types.h>
@ -28,37 +29,6 @@
namespace android {
namespace vold {
enum class VolumeState {
kUnmounted,
kMounting,
kMounted,
kCorrupt,
kFormatting,
kUnmounting,
};
enum class VolumeType {
kPublic,
kPrivate,
kEmulated,
kAsec,
kObb,
};
// events:
// volume_created private:127:4
// volume_state private:127:4 mounted
// volume_meta private:127:4 [fsGuid] [label]
// volume_destroyed public:127:4
// commands:
// volume mount public:127:4 [primary]
// volume unmount public:127:4
// volume bind_user public:127:4 [userId]
// volume unbind_user public:127:4 [userId]
// volume bind_package private:4:1 [userId] [package]
// volume unbind_package private:4:1 [userId] [package]
/*
* Representation of a mounted volume ready for presentation.
*
@ -77,43 +47,81 @@ class VolumeBase {
public:
virtual ~VolumeBase();
VolumeType getType() { return mType; }
enum class Type {
kPublic = 0,
kPrivate,
kEmulated,
kAsec,
kObb,
};
enum Flags {
/* Flag that volume is primary external storage */
kPrimary = 1 << 0,
/* Flag that volume is visible to normal apps */
kVisible = 1 << 1,
};
enum class State {
kUnmounted = 0,
kMounting,
kMounted,
kFormatting,
kUnmounting,
};
const std::string& getId() { return mId; }
VolumeState getState() { return mState; }
Type getType() { return mType; }
int getFlags() { return mFlags; }
userid_t getUser() { return mUser; }
State getState() { return mState; }
const std::string& getPath() { return mPath; }
status_t setFlags(int flags);
status_t setUser(userid_t user);
void stackVolume(const std::shared_ptr<VolumeBase>& volume);
void unstackVolume(const std::shared_ptr<VolumeBase>& volume);
void addVolume(const std::shared_ptr<VolumeBase>& volume);
void removeVolume(const std::shared_ptr<VolumeBase>& volume);
std::shared_ptr<VolumeBase> findVolume(const std::string& id);
status_t create();
status_t destroy();
status_t mount();
status_t unmount();
status_t format();
protected:
explicit VolumeBase(VolumeType type);
/* ID that uniquely references this disk */
std::string mId;
/* Manage bind mounts for this volume */
status_t mountBind(const std::string& source, const std::string& target);
status_t unmountBind(const std::string& target);
explicit VolumeBase(Type type);
virtual status_t doMount() = 0;
virtual status_t doUnmount() = 0;
virtual status_t doFormat();
status_t setId(const std::string& id);
status_t setPath(const std::string& path);
private:
/* ID that uniquely references volume while alive */
std::string mId;
/* Volume type */
VolumeType mType;
Type mType;
/* Flags applicable to volume */
int mFlags;
/* User that owns this volume, otherwise -1 */
userid_t mUser;
/* Flag indicating object is created */
bool mCreated;
/* Current state of volume */
VolumeState mState;
State mState;
/* Path to mounted volume */
std::string mPath;
/* Volumes stacked on top of this volume */
std::list<std::shared_ptr<VolumeBase>> mStacked;
/* Currently active bind mounts */
std::list<std::string> mBindTargets;
std::list<std::shared_ptr<VolumeBase>> mVolumes;
void setState(VolumeState state);
void setState(State state);
DISALLOW_COPY_AND_ASSIGN(VolumeBase);
};

@ -34,6 +34,8 @@
#include <openssl/md5.h>
#include <base/logging.h>
#include <base/stringprintf.h>
#include <cutils/fs.h>
#include <cutils/log.h>
@ -43,23 +45,35 @@
#include <private/android_filesystem_config.h>
#include "EmulatedVolume.h"
#include "VolumeManager.h"
#include "NetlinkManager.h"
#include "DirectVolume.h"
#include "ResponseCode.h"
#include "Loop.h"
#include "Ext4.h"
#include "Fat.h"
#include "Utils.h"
#include "Devmapper.h"
#include "Process.h"
#include "Asec.h"
#include "VoldUtil.h"
#include "cryptfs.h"
#define DEBUG_NETLINK 0
#define MASS_STORAGE_FILE_PATH "/sys/class/android_usb/android0/f_mass_storage/lun/file"
#define ROUND_UP_POWER_OF_2(number, po2) (((!!(number & ((1U << po2) - 1))) << po2)\
+ (number & (~((1U << po2) - 1))))
using android::base::StringPrintf;
static const char* kUserMountPath = "/mnt/user";
static const unsigned int kMajorBlockScsi = 8;
static const unsigned int kMajorBlockMmc = 179;
/* writes superblock at end of file or device given by name */
static int writeSuperBlock(const char* name, struct asec_superblock *sb, unsigned int numImgSectors) {
int sbfd = open(name, O_RDWR);
@ -227,10 +241,45 @@ void VolumeManager::setDebug(bool enable) {
}
int VolumeManager::start() {
// Always start from a clean slate by unmounting everything in
// directories that we own, in case we crashed.
FILE* fp = setmntent("/proc/mounts", "r");
if (fp == NULL) {
SLOGE("Error opening /proc/mounts: %s", strerror(errno));
return -errno;
}
// Some volumes can be stacked on each other, so force unmount in
// reverse order to give us the best chance of success.
std::list<std::string> toUnmount;
mntent* mentry;
while ((mentry = getmntent(fp)) != NULL) {
if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0
|| strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {
toUnmount.push_front(std::string(mentry->mnt_dir));
}
}
endmntent(fp);
for (auto path : toUnmount) {
SLOGW("Tearing down stale mount %s", path.c_str());
android::vold::ForceUnmount(path);
}
// TODO: nuke all files under mnt and storage tmpfs too?
// Assume that we always have an emulated volume on internal
// storage; the framework will decide if it should be mounted.
mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
new android::vold::EmulatedVolume("/data/media", ""));
mInternalEmulated->create();
return 0;
}
int VolumeManager::stop() {
mInternalEmulated->destroy();
mInternalEmulated = nullptr;
return 0;
}
@ -240,28 +289,162 @@ int VolumeManager::addVolume(Volume *v) {
}
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
#ifdef NETLINK_DEBUG
const char *devpath = evt->findParam("DEVPATH");
#if DEBUG_NETLINK
LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();
evt->dump();
#endif
/* Lookup a volume to handle this device */
VolumeCollection::iterator it;
bool hit = false;
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
if (!(*it)->handleBlockEvent(evt)) {
#ifdef NETLINK_DEBUG
SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
#endif
hit = true;
break;
std::string eventPath(evt->findParam("DEVPATH"));
std::string devType(evt->findParam("DEVTYPE"));
if (devType != "disk") return;
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
dev_t device = makedev(major, minor);
switch (evt->getAction()) {
case NetlinkEvent::Action::kAdd: {
for (auto source : mDiskSources) {
if (source->matches(eventPath)) {
// For now, assume that MMC devices are SD, and that
// everything else is USB
int flags = source->getFlags();
if (major == kMajorBlockMmc) {
flags |= android::vold::Disk::Flags::kSd;
} else {
flags |= android::vold::Disk::Flags::kUsb;
}
auto disk = new android::vold::Disk(eventPath, device,
source->getNickname(), flags);
disk->create();
mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));
break;
}
}
break;
}
case NetlinkEvent::Action::kChange: {
for (auto disk : mDisks) {
if (disk->getDevice() == device) {
disk->readMetadata();
disk->readPartitions();
}
}
break;
}
case NetlinkEvent::Action::kRemove: {
auto i = mDisks.begin();
while (i != mDisks.end()) {
if ((*i)->getDevice() == device) {
(*i)->destroy();
i = mDisks.erase(i);
} else {
++i;
}
}
break;
}
default: {
LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();
break;
}
}
}
if (!hit) {
#ifdef NETLINK_DEBUG
SLOGW("No volumes handled block event for '%s'", devpath);
#endif
void VolumeManager::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) {
mDiskSources.push_back(diskSource);
}
std::shared_ptr<android::vold::Disk> VolumeManager::findDisk(const std::string& id) {
for (auto disk : mDisks) {
if (disk->getId() == id) {
return disk;
}
}
return nullptr;
}
std::shared_ptr<android::vold::VolumeBase> VolumeManager::findVolume(const std::string& id) {
if (mInternalEmulated->getId() == id) {
return mInternalEmulated;
}
for (auto disk : mDisks) {
auto vol = disk->findVolume(id);
if (vol != nullptr) {
return vol;
}
}
return nullptr;
}
int VolumeManager::linkPrimary(userid_t userId) {
std::string source(mPrimary->getPath());
if (mPrimary->getType() == android::vold::VolumeBase::Type::kEmulated) {
source = StringPrintf("%s/%d", source.c_str(), userId);
}
std::string target(StringPrintf("/mnt/user/%d/primary", userId));
if (TEMP_FAILURE_RETRY(unlink(target.c_str()))) {
if (errno != ENOENT) {
SLOGW("Failed to unlink %s: %s", target.c_str(), strerror(errno));
}
}
if (TEMP_FAILURE_RETRY(symlink(source.c_str(), target.c_str()))) {
SLOGW("Failed to link %s to %s: %s", source.c_str(), target.c_str(),
strerror(errno));
return -errno;
}
return 0;
}
int VolumeManager::startUser(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));
fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
mUsers.push_back(userId);
if (mPrimary) {
linkPrimary(userId);
}
return 0;
}
int VolumeManager::cleanupUser(userid_t userId) {
mUsers.remove(userId);
return 0;
}
int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
mPrimary = vol;
for (userid_t userId : mUsers) {
linkPrimary(userId);
}
return 0;
}
int VolumeManager::reset() {
// Tear down all existing disks/volumes and start from a blank slate so
// newly connected framework hears all events.
mInternalEmulated->destroy();
mInternalEmulated->create();
for (auto disk : mDisks) {
disk->destroy();
disk->create();
}
mUsers.clear();
return 0;
}
int VolumeManager::shutdown() {
for (auto disk : mDisks) {
disk->destroy();
}
mDisks.clear();
return 0;
}
int VolumeManager::listVolumes(SocketClient *cli, bool broadcast) {
@ -1060,16 +1243,16 @@ int VolumeManager::unmountLoopImage(const char *id, const char *idHash,
SLOGW("%s unmount attempt %d failed (%s)",
id, i, strerror(errno));
int action = 0; // default is to just complain
int signal = 0; // default is to just complain
if (force) {
if (i > (UNMOUNT_RETRIES - 2))
action = 2; // SIGKILL
signal = SIGKILL;
else if (i > (UNMOUNT_RETRIES - 3))
action = 1; // SIGHUP
signal = SIGTERM;
}
Process::killProcessesWithOpenFiles(mountPoint, action);
Process::killProcessesWithOpenFiles(mountPoint, signal);
usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
}
@ -1867,23 +2050,12 @@ int VolumeManager::cleanupAsec(Volume *v, bool force) {
}
int VolumeManager::mkdirs(char* path) {
// Require that path lives under a volume we manage and is mounted
const char* emulated_source = getenv("EMULATED_STORAGE_SOURCE");
const char* root = NULL;
if (emulated_source && !strncmp(path, emulated_source, strlen(emulated_source))) {
root = emulated_source;
// Only offer to create directories for paths managed by vold
if (strncmp(path, "/storage/", 9) == 0) {
// fs_mkdirs() does symlink checking and relative path enforcement
return fs_mkdirs(path, 0700);
} else {
Volume* vol = getVolumeForFile(path);
if (vol && vol->getState() == Volume::State_Mounted) {
root = vol->getMountpoint();
}
}
if (!root) {
SLOGE("Failed to find mounted volume for %s", path);
return -EINVAL;
}
/* fs_mkdirs() does symlink checking and relative path enforcement */
return fs_mkdirs(path, 0700);
}

@ -14,16 +14,25 @@
* limitations under the License.
*/
#ifndef _VOLUMEMANAGER_H
#define _VOLUMEMANAGER_H
#ifndef ANDROID_VOLD_VOLUME_MANAGER_H
#define ANDROID_VOLD_VOLUME_MANAGER_H
#include <pthread.h>
#include <fnmatch.h>
#include <stdlib.h>
#ifdef __cplusplus
#include <string>
#include <list>
#include <cutils/multiuser.h>
#include <utils/List.h>
#include <sysutils/SocketListener.h>
#include "Disk.h"
#include "Volume.h"
#include "VolumeBase.h"
/* The length of an MD5 hash when encoded into ASCII hex characters */
#define MD5_ASCII_LENGTH_PLUS_NULL ((MD5_DIGEST_LENGTH*2)+1)
@ -54,7 +63,6 @@ class VolumeManager {
private:
static VolumeManager *sInstance;
private:
SocketListener *mBroadcaster;
VolumeCollection *mVolumes;
@ -77,6 +85,40 @@ public:
int addVolume(Volume *v);
class DiskSource {
public:
DiskSource(const std::string& sysPattern, const std::string& nickname, int flags) :
mSysPattern(sysPattern), mNickname(nickname), mFlags(flags) {
}
bool matches(const std::string& sysPath) {
return !fnmatch(mSysPattern.c_str(), sysPath.c_str(), 0);
}
const std::string& getNickname() { return mNickname; }
int getFlags() { return mFlags; }
private:
std::string mSysPattern;
std::string mNickname;
int mFlags;
};
void addDiskSource(const std::shared_ptr<DiskSource>& diskSource);
std::shared_ptr<android::vold::Disk> findDisk(const std::string& id);
std::shared_ptr<android::vold::VolumeBase> findVolume(const std::string& id);
int startUser(userid_t userId);
int cleanupUser(userid_t userId);
int setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol);
/* Reset all internal state, typically during framework boot */
int reset();
/* Prepare for device shutdown, safely unmounting all devices */
int shutdown();
int listVolumes(SocketClient *cli, bool broadcast);
int mountVolume(const char *label);
int unmountVolume(const char *label, bool force, bool revert);
@ -156,6 +198,16 @@ private:
bool isMountpointMounted(const char *mp);
bool isAsecInDirectory(const char *dir, const char *asec) const;
bool isLegalAsecId(const char *id) const;
int linkPrimary(userid_t userId);
std::list<std::shared_ptr<DiskSource>> mDiskSources;
std::list<std::shared_ptr<android::vold::Disk>> mDisks;
std::list<userid_t> mUsers;
std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
std::shared_ptr<android::vold::VolumeBase> mPrimary;
};
extern "C" {

@ -1451,10 +1451,10 @@ static int wait_and_unmount(char *mountpoint, bool kill)
if (kill) {
if (i == (WAIT_UNMOUNT_COUNT - 3)) {
SLOGW("sending SIGHUP to processes with open files\n");
vold_killProcessesWithOpenFiles(mountpoint, 1);
vold_killProcessesWithOpenFiles(mountpoint, SIGTERM);
} else if (i == (WAIT_UNMOUNT_COUNT - 2)) {
SLOGW("sending SIGKILL to processes with open files\n");
vold_killProcessesWithOpenFiles(mountpoint, 2);
vold_killProcessesWithOpenFiles(mountpoint, SIGKILL);
}
}
@ -3062,6 +3062,9 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
property_get("ro.crypto.fuse_sdcard", fuse_sdcard, "");
if (!strcmp(fuse_sdcard, "true")) {
// STOPSHIP: UNMOUNT ALL STORAGE BEFORE REACHING HERE, SINCE VOLD NOW MANAGES FUSE
// "ro.crypto.fuse_sdcard" is now deprecated
/* This is a device using the fuse layer to emulate the sdcard semantics
* on top of the userdata partition. vold does not manage it, it is managed
* by the sdcard service. The sdcard service was killed by the property trigger

@ -27,10 +27,13 @@
#define LOG_TAG "Vold"
#include <base/logging.h>
#include <base/stringprintf.h>
#include "cutils/klog.h"
#include "cutils/log.h"
#include "cutils/properties.h"
#include "Disk.h"
#include "VolumeManager.h"
#include "CommandListener.h"
#include "NetlinkManager.h"
@ -41,12 +44,17 @@
static int process_config(VolumeManager *vm);
static void coldboot(const char *path);
#define FSTAB_PREFIX "/fstab."
//#define DEBUG_FSTAB "/data/local/tmp/fstab.debug"
struct fstab *fstab;
struct selabel_handle *sehandle;
int main() {
using android::base::StringPrintf;
int main(int argc, char* argv[]) {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv);
VolumeManager *vm;
CommandListener *cl;
@ -159,54 +167,47 @@ static void coldboot(const char *path)
}
static int process_config(VolumeManager *vm)
{
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char propbuf[PROPERTY_VALUE_MAX];
int i;
int ret = -1;
int flags;
property_get("ro.hardware", propbuf, "");
snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
{
char hardware[PROPERTY_VALUE_MAX];
property_get("ro.hardware", hardware, "");
std::string fstab_filename(StringPrintf("/fstab.%s", hardware));
#ifdef DEBUG_FSTAB
if (access(DEBUG_FSTAB, R_OK) == 0) {
LOG(DEBUG) << "Found debug fstab; switching!";
fstab_filename = DEBUG_FSTAB;
}
#endif
fstab = fs_mgr_read_fstab(fstab_filename);
fstab = fs_mgr_read_fstab(fstab_filename.c_str());
if (!fstab) {
SLOGE("failed to open %s\n", fstab_filename);
PLOG(ERROR) << "Failed to open " << fstab_filename;
return -1;
}
/* Loop through entries looking for ones that vold manages */
for (i = 0; i < fstab->num_entries; i++) {
for (int i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
DirectVolume *dv = NULL;
flags = 0;
/* Set any flags that might be set for this volume */
if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
flags |= VOL_NONREMOVABLE;
LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
continue;
}
std::string sysPattern(fstab->recs[i].blk_device);
std::string nickname(fstab->recs[i].label);
int flags = 0;
if (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= VOL_ENCRYPTABLE;
flags |= android::vold::Disk::Flags::kAdoptable;
}
/* Only set this flag if there is not an emulated sd card */
if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&
!strcmp(fstab->recs[i].fs_type, "vfat")) {
flags |= VOL_PROVIDES_ASEC;
if (fs_mgr_is_noemulatedsd(&fstab->recs[i])) {
flags |= android::vold::Disk::Flags::kDefaultPrimary;
}
dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
if (dv->addPath(fstab->recs[i].blk_device)) {
SLOGE("Failed to add devpath %s to volume %s",
fstab->recs[i].blk_device, fstab->recs[i].label);
goto out_fail;
}
vm->addVolume(dv);
vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
}
ret = 0;
out_fail:
return ret;
return 0;
}

Loading…
Cancel
Save