vold2: Initial support for Android Secure External Caches

Signed-off-by: San Mehat <san@google.com>
This commit is contained in:
San Mehat 2010-01-06 10:33:53 -08:00
parent 7b8f2db4b0
commit a19b250bd2
11 changed files with 663 additions and 7 deletions

View file

@ -22,7 +22,8 @@ LOCAL_SRC_FILES:= \
logwrapper.c \
ProcessKiller.c \
geom_mbr_enc.c \
Fat.cpp
Fat.cpp \
Loop.cpp
LOCAL_MODULE:= vold

View file

@ -16,8 +16,10 @@
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#define LOG_TAG "CommandListener"
@ -39,6 +41,12 @@ CommandListener::CommandListener() :
registerCmd(new ShareAvailableCmd());
registerCmd(new SimulateCmd());
registerCmd(new FormatCmd());
registerCmd(new CreateAsecCmd());
registerCmd(new FinalizeAsecCmd());
registerCmd(new DestroyAsecCmd());
registerCmd(new MountAsecCmd());
registerCmd(new ListAsecCmd());
registerCmd(new AsecPathCmd());
}
CommandListener::ListVolumesCmd::ListVolumesCmd() :
@ -160,3 +168,142 @@ int CommandListener::FormatCmd::runCommand(SocketClient *cli,
return 0;
}
CommandListener::CreateAsecCmd::CreateAsecCmd() :
VoldCommand("create_asec") {
}
int CommandListener::CreateAsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc != 6) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: create_asec <namespace-id> <size_mb> <fstype> <key> <ownerUid>",
false);
return 0;
}
if (VolumeManager::Instance()->createAsec(argv[1], atoi(argv[2]),
argv[3], argv[4],
atoi(argv[5]))) {
cli->sendMsg(ResponseCode::OperationFailed, "Cache creation failed", true);
} else {
cli->sendMsg(ResponseCode::CommandOkay, "Cache created", false);
}
return 0;
}
CommandListener::FinalizeAsecCmd::FinalizeAsecCmd() :
VoldCommand("finalize_asec") {
}
int CommandListener::FinalizeAsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: finalize_asec <namespace-id>", false);
return 0;
}
if (VolumeManager::Instance()->finalizeAsec(argv[1])) {
cli->sendMsg(ResponseCode::OperationFailed, "Cache finalize failed", true);
} else {
cli->sendMsg(ResponseCode::CommandOkay, "Cache finalized", false);
}
return 0;
}
CommandListener::DestroyAsecCmd::DestroyAsecCmd() :
VoldCommand("destroy_asec") {
}
int CommandListener::DestroyAsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: destroy_asec <namespace-id>", false);
return 0;
}
if (VolumeManager::Instance()->destroyAsec(argv[1])) {
cli->sendMsg(ResponseCode::OperationFailed, "Destroy failed", true);
} else {
cli->sendMsg(ResponseCode::CommandOkay, "Cache Destroyed", false);
}
return 0;
}
CommandListener::MountAsecCmd::MountAsecCmd() :
VoldCommand("mount_asec") {
}
int CommandListener::MountAsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: mount_asec <namespace-id> <key> <ownerUid>", false);
return 0;
}
if (VolumeManager::Instance()->mountAsec(argv[1], argv[2], atoi(argv[3]))) {
cli->sendMsg(ResponseCode::OperationFailed, "Mount failed", true);
} else {
cli->sendMsg(ResponseCode::CommandOkay, "Mount succeeded", false);
}
return 0;
}
CommandListener::ListAsecCmd::ListAsecCmd() :
VoldCommand("list_asec") {
}
int CommandListener::ListAsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
DIR *d = opendir("/sdcard/android_secure");
if (!d) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
return 0;
}
struct dirent *dent;
while ((dent = readdir(d))) {
if (dent->d_name[0] == '.')
continue;
if (!strcmp(&dent->d_name[strlen(dent->d_name)-5], ".asec")) {
char id[255];
memset(id, 0, sizeof(id));
strncpy(id, dent->d_name, strlen(dent->d_name) -5);
cli->sendMsg(ResponseCode::AsecListResult, id, false);
}
}
closedir(d);
cli->sendMsg(ResponseCode::CommandOkay, "ASEC listing complete", false);
return 0;
}
CommandListener::AsecPathCmd::AsecPathCmd() :
VoldCommand("asec_path") {
}
int CommandListener::AsecPathCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
"Usage: asec_path <namespace-id>", false);
return 0;
}
char mountPath[255];
if (VolumeManager::Instance()->getAsecMountPath(argv[1], mountPath,
sizeof(mountPath))) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to get mount path", true);
} else {
cli->sendMsg(ResponseCode::AsecPathResult, mountPath, false);
}
return 0;
}

View file

@ -83,6 +83,49 @@ private:
int runCommand(SocketClient *c, int argc, char ** argv);
};
class CreateAsecCmd : public VoldCommand {
public:
CreateAsecCmd();
virtual ~CreateAsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
class FinalizeAsecCmd : public VoldCommand {
public:
FinalizeAsecCmd();
virtual ~FinalizeAsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
class DestroyAsecCmd : public VoldCommand {
public:
DestroyAsecCmd();
virtual ~DestroyAsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
class MountAsecCmd : public VoldCommand {
public:
MountAsecCmd();
virtual ~MountAsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
class ListAsecCmd : public VoldCommand {
public:
ListAsecCmd();
virtual ~ListAsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
class AsecPathCmd : public VoldCommand {
public:
AsecPathCmd();
virtual ~AsecPathCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
};
#endif

View file

@ -92,12 +92,15 @@ int Fat::check(const char *fsPath) {
return 0;
}
int Fat::doMount(const char *fsPath, const char *mountPoint) {
int Fat::doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount) {
int rc;
unsigned long flags;
flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
flags |= (ro ? MS_RDONLY : 0);
flags |= (remount ? MS_REMOUNT : 0);
/*
* Note: This is a temporary hack. If the sampling profiler is enabled,
* we make the SD card world-writable so any process can write snapshots.
@ -168,7 +171,9 @@ int Fat::format(const char *fsPath) {
int rc;
args[0] = MKDOSFS_PATH;
args[1] = "-F";
if ((nr_sec * 512) <= ((unsigned int) (1024*1024*1024) * 2))
if ((nr_sec * 512) <= ((unsigned int) (1024*1024) * 32))
args[2] = "12";
else if ((nr_sec * 512) <= ((unsigned int) (1024*1024*1024) * 2))
args[2] = "16";
else
args[2] = "32";

3
Fat.h
View file

@ -22,7 +22,8 @@
class Fat {
public:
static int check(const char *fsPath);
static int doMount(const char *fsPath, const char *mountPoint);
static int doMount(const char *fsPath, const char *mountPoint, bool ro,
bool remount);
static int format(const char *fsPath);
};

195
Loop.cpp Normal file
View file

@ -0,0 +1,195 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define LOG_TAG "Vold"
#include <cutils/log.h>
#include "Loop.h"
int Loop::lookupActive(const char *loopFile, char *buffer, size_t len) {
int i;
int fd;
char filename[256];
memset(buffer, 0, len);
for (i = 0; i < LOOP_MAX; i++) {
struct loop_info li;
int rc;
sprintf(filename, "/dev/block/loop%d", i);
if ((fd = open(filename, O_RDWR)) < 0) {
LOGE("Unable to open %s (%s)", filename, strerror(errno));
return -1;
}
rc = ioctl(fd, LOOP_GET_STATUS, &li);
close(fd);
if (rc < 0 && errno == ENXIO) {
continue;
break;
}
if (rc < 0) {
LOGE("Unable to get loop status for %s (%s)", filename,
strerror(errno));
return -1;
}
if (!strncmp(li.lo_name, loopFile, LO_NAME_SIZE)) {
break;
}
}
if (i == LOOP_MAX) {
errno = ENOENT;
return -1;
}
strncpy(buffer, filename, len -1);
return 0;
}
int Loop::getNextAvailable(char *buffer, size_t len) {
int i;
int fd;
char filename[256];
memset(buffer, 0, len);
for (i = 0; i < LOOP_MAX; i++) {
struct loop_info li;
int rc;
sprintf(filename, "/dev/block/loop%d", i);
if ((fd = open(filename, O_RDWR)) < 0) {
LOGE("Unable to open %s (%s)", filename, strerror(errno));
return -1;
}
rc = ioctl(fd, LOOP_GET_STATUS, &li);
close(fd);
if (rc < 0 && errno == ENXIO)
break;
if (rc < 0) {
LOGE("Unable to get loop status for %s (%s)", filename,
strerror(errno));
return -1;
}
}
if (i == LOOP_MAX) {
LOGE("Exhausted all loop devices");
errno = ENOSPC;
return -1;
}
strncpy(buffer, filename, len -1);
return 0;
}
int Loop::create(const char *loopDevice, const char *loopFile) {
int fd;
int file_fd;
LOGD("Creating loop for file '%s' into loop device '%s'", loopFile,
loopDevice);
if ((fd = open(loopDevice, O_RDWR)) < 0) {
LOGE("Unable to open loop device %s (%s)", loopDevice,
strerror(errno));
return -1;
}
if ((file_fd = open(loopFile, O_RDWR)) < 0) {
LOGE("Unable to open %s (%s)", loopFile, strerror(errno));
close(fd);
return -1;
}
if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
LOGE("Error setting up loopback interface (%s)", strerror(errno));
close(file_fd);
close(fd);
return -1;
}
struct loop_info li;
memset(&li, 0, sizeof(li));
strncpy(li.lo_name, loopFile, LO_NAME_SIZE);
if (ioctl(fd, LOOP_SET_STATUS, &li) < 0) {
LOGE("Error setting loopback status (%s)", strerror(errno));
close(file_fd);
close(fd);
return -1;
}
close(fd);
close(file_fd);
return 0;
}
int Loop::destroyByDevice(const char *loopDevice) {
int device_fd;
device_fd = open(loopDevice, O_RDONLY);
if (device_fd < 0) {
LOGE("Failed to open loop (%d)", errno);
return -1;
}
if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
LOGE("Failed to destroy loop (%d)", errno);
close(device_fd);
return -1;
}
close(device_fd);
return 0;
}
int Loop::destroyByFile(const char *loopFile) {
errno = ENOSYS;
return -1;
}
int Loop::createImageFile(const char *file, size_t sizeMb) {
int fd;
LOGD("Creating ASEC image file %s (%d mb)", file, sizeMb);
if ((fd = creat(file, 0600)) < 0) {
LOGE("Error creating imagefile (%s)", strerror(errno));
return -1;
}
if (ftruncate(fd, (sizeMb * (1024 * 1024))) < 0) {
LOGE("Error truncating imagefile (%s)", strerror(errno));
close(fd);
return -1;
}
close(fd);
return 0;
}

36
Loop.h Normal file
View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LOOP_H
#define _LOOP_H
#include <unistd.h>
#include <linux/loop.h>
class Loop {
public:
static const int LOOP_MAX = 8;
public:
static int getNextAvailable(char *buffer, size_t len);
static int lookupActive(const char *loopFile, char *buffer, size_t len);
static int create(const char *loopDevice, const char *loopFile);
static int destroyByDevice(const char *loopDevice);
static int destroyByFile(const char *loopFile);
static int createImageFile(const char *file, size_t sizeMb);
};
#endif

View file

@ -21,13 +21,15 @@ class ResponseCode {
public:
// 100 series - Requestion action was initiated; expect another reply
// before proceeding with a new command.
static const int ActionInitiated = 100;
static const int ActionInitiated = 100;
static const int VolumeListResult = 110;
static const int AsecListResult = 111;
// 200 series - Requested action has been successfully completed
static const int CommandOkay = 200;
static const int CommandOkay = 200;
static const int ShareAvailabilityResult = 210;
static const int AsecPathResult = 211;
// 400 series - The command was accepted but the requested action
// did not take place.

View file

@ -268,7 +268,7 @@ int Volume::mountVol() {
LOGI("%s checks out - attempting to mount\n", devicePath);
errno = 0;
if (!(rc = Fat::doMount(devicePath, getMountpoint()))) {
if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false))) {
LOGI("%s sucessfully mounted for volume %s\n", devicePath,
getLabel());
setState(Volume::State_Mounted);

View file

@ -19,6 +19,10 @@
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <linux/kdev_t.h>
#define LOG_TAG "Vold"
@ -30,6 +34,8 @@
#include "VolumeManager.h"
#include "DirectVolume.h"
#include "ResponseCode.h"
#include "Loop.h"
#include "Fat.h"
VolumeManager *VolumeManager::sInstance = NULL;
@ -149,6 +155,191 @@ int VolumeManager::formatVolume(const char *label) {
return v->formatVol();
}
int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
char mountPoint[255];
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
if (!isMountpointMounted(mountPoint)) {
errno = ENOENT;
return -1;
}
snprintf(buffer, maxlen, "/asec/%s", id);
return 0;
}
int VolumeManager::createAsec(const char *id, int sizeMb,
const char *fstype, const char *key, int ownerUid) {
mkdir("/sdcard/android_secure", 0777);
if (lookupVolume(id)) {
LOGE("ASEC volume '%s' currently exists", id);
errno = EADDRINUSE;
return -1;
}
char asecFileName[255];
snprintf(asecFileName, sizeof(asecFileName),
"/sdcard/android_secure/%s.asec", id);
if (!access(asecFileName, F_OK)) {
LOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
asecFileName, strerror(errno));
errno = EADDRINUSE;
return -1;
}
if (Loop::createImageFile(asecFileName, sizeMb)) {
LOGE("ASEC image file creation failed (%s)", strerror(errno));
return -1;
}
char loopDevice[255];
if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
unlink(asecFileName);
return -1;
}
if (Loop::create(loopDevice, asecFileName)) {
LOGE("ASEC loop device creation failed (%s)", strerror(errno));
unlink(asecFileName);
return -1;
}
/* XXX: Start devmapper */
if (Fat::format(loopDevice)) {
LOGE("ASEC FAT format failed (%s)", strerror(errno));
Loop::destroyByDevice(loopDevice);
unlink(asecFileName);
return -1;
}
char mountPoint[255];
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
if (mkdir(mountPoint, 0777)) {
LOGE("Mountpoint creation failed (%s)", strerror(errno));
Loop::destroyByDevice(loopDevice);
unlink(asecFileName);
return -1;
}
if (Fat::doMount(loopDevice, mountPoint, false, false)) {
LOGE("ASEC FAT mount failed (%s)", strerror(errno));
Loop::destroyByDevice(loopDevice);
unlink(asecFileName);
return -1;
}
return 0;
}
int VolumeManager::finalizeAsec(const char *id) {
char asecFileName[255];
char loopDevice[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName),
"/sdcard/android_secure/%s.asec", id);
if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
LOGE("Unable to finalize %s (%s)", id, strerror(errno));
return -1;
}
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
if (Fat::doMount(loopDevice, mountPoint, true, true)) {
LOGE("ASEC finalize mount failed (%s)", strerror(errno));
return -1;
}
LOGD("ASEC %s finalized", id);
return 0;
}
int VolumeManager::destroyAsec(const char *id) {
char asecFileName[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName),
"/sdcard/android_secure/%s.asec", id);
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
if (isMountpointMounted(mountPoint)) {
int i, rc;
for (i = 0; i < 10; i++) {
rc = umount(mountPoint);
if (!rc) {
break;
}
if (rc && (errno == EINVAL || errno == ENOENT)) {
rc = 0;
break;
}
LOGW("ASEC %s unmount attempt %d failed (%s)",
id, i +1, strerror(errno));
usleep(1000 * 250);
}
if (rc) {
LOGE("Failed to unmount ASEC %s for destroy", id);
return -1;
}
}
char loopDevice[255];
if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
Loop::destroyByDevice(loopDevice);
}
unlink(asecFileName);
LOGD("ASEC %s destroyed", id);
return 0;
}
int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
char asecFileName[255];
char mountPoint[255];
snprintf(asecFileName, sizeof(asecFileName),
"/sdcard/android_secure/%s.asec", id);
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
if (isMountpointMounted(mountPoint)) {
LOGE("ASEC %s already mounted", id);
errno = EBUSY;
return -1;
}
char loopDevice[255];
if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
LOGE("Unable to find loop device for ASEC mount");
return -1;
}
if (Loop::create(loopDevice, asecFileName)) {
LOGE("ASEC loop device creation failed (%s)", strerror(errno));
return -1;
}
}
if (mkdir(mountPoint, 0777)) {
LOGE("Mountpoint creation failed (%s)", strerror(errno));
return -1;
}
if (Fat::doMount(loopDevice, mountPoint, true, false)) {
LOGE("ASEC mount failed (%s)", strerror(errno));
return -1;
}
LOGD("ASEC %s mounted", id);
return 0;
}
int VolumeManager::mountVolume(const char *label) {
Volume *v = lookupVolume(label);
@ -334,3 +525,31 @@ Volume *VolumeManager::lookupVolume(const char *label) {
}
return NULL;
}
bool VolumeManager::isMountpointMounted(const char *mp)
{
char device[256];
char mount_path[256];
char rest[256];
FILE *fp;
char line[1024];
if (!(fp = fopen("/proc/mounts", "r"))) {
LOGE("Error opening /proc/mounts (%s)", strerror(errno));
return false;
}
while(fgets(line, sizeof(line), fp)) {
line[strlen(line)-1] = '\0';
sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
if (!strcmp(mount_path, mp)) {
fclose(fp);
return true;
}
}
fclose(fp);
return false;
}

View file

@ -55,6 +55,12 @@ public:
int shareAvailable(const char *method, bool *avail);
int simulate(const char *cmd, const char *arg);
int formatVolume(const char *label);
int createAsec(const char *id, int sizeMb, const char *fstype,
const char *key, int ownerUid);
int finalizeAsec(const char *id);
int destroyAsec(const char *id);
int mountAsec(const char *id, const char *key, int ownerUid);
int getAsecMountPath(const char *id, char *buffer, int maxlen);
// XXX: This should be moved private once switch uevents are working
void notifyUmsConnected(bool connected);
@ -67,5 +73,6 @@ public:
private:
VolumeManager();
Volume *lookupVolume(const char *label);
bool isMountpointMounted(const char *mp);
};
#endif