am 4468682c: Merge from open-source master
Merge commit '4468682c7035d431c6154ab298c5ce0f9e2350f8'
* commit '4468682c7035d431c6154ab298c5ce0f9e2350f8':
new project, first commit
diff --git a/Android.mk b/Android.mk
index dda81ed..d90b399 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,4 +1,8 @@
BUILD_VOLD2 := false
+ifneq ($(TARGET_SIMULATOR),true)
+ BUILD_VOLD2 := true
+endif
+
ifeq ($(BUILD_VOLD2),true)
LOCAL_PATH:= $(call my-dir)
@@ -15,7 +19,12 @@
BlockDevice.cpp \
Volume.cpp \
DirectVolume.cpp \
- logwrapper.c
+ logwrapper.c \
+ ProcessKiller.c \
+ geom_mbr_enc.c \
+ Fat.cpp \
+ Loop.cpp \
+ Devmapper.cpp
LOCAL_MODULE:= vold
@@ -23,7 +32,7 @@
LOCAL_CFLAGS :=
-LOCAL_SHARED_LIBRARIES := libsysutils
+LOCAL_SHARED_LIBRARIES := libsysutils libcutils
include $(BUILD_EXECUTABLE)
diff --git a/CommandListener.cpp b/CommandListener.cpp
index a7bc807..59c431f 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -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"
@@ -27,78 +29,230 @@
#include "CommandListener.h"
#include "VolumeManager.h"
-#include "ErrorCode.h"
+#include "ResponseCode.h"
CommandListener::CommandListener() :
FrameworkListener("vold") {
- registerCmd(new ListVolumesCmd());
- registerCmd(new MountVolumeCmd());
- registerCmd(new UnmountVolumeCmd());
- registerCmd(new ShareVolumeCmd());
- registerCmd(new UnshareVolumeCmd());
+ registerCmd(new VolumeCmd());
+ registerCmd(new AsecCmd());
+ registerCmd(new ShareCmd());
}
-CommandListener::ListVolumesCmd::ListVolumesCmd() :
- VoldCommand("list_volumes") {
+CommandListener::VolumeCmd::VolumeCmd() :
+ VoldCommand("volume") {
}
-int CommandListener::ListVolumesCmd::runCommand(SocketClient *cli,
+int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
- return VolumeManager::Instance()->listVolumes(cli);
-}
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+ return 0;
+ }
-CommandListener::MountVolumeCmd::MountVolumeCmd() :
- VoldCommand("mount_volume") {
-}
+ VolumeManager *vm = VolumeManager::Instance();
+ int rc = 0;
-int CommandListener::MountVolumeCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- /* Synchronously mount a volume */
- if (VolumeManager::Instance()->mountVolume(argv[1])) {
- cli->sendMsg(ErrorCode::OperationFailed, "Failed to mount volume.", true);
+ if (!strcmp(argv[1], "list")) {
+ return vm->listVolumes(cli);
+ } else if (!strcmp(argv[1], "mount")) {
+ rc = vm->mountVolume(argv[2]);
+ } else if (!strcmp(argv[1], "unmount")) {
+ rc = vm->unmountVolume(argv[2]);
+ } else if (!strcmp(argv[1], "format")) {
+ rc = vm->formatVolume(argv[2]);
+ } else if (!strcmp(argv[1], "share")) {
+ rc = vm->shareVolume(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "unshare")) {
+ rc = vm->unshareVolume(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "shared")) {
+ bool enabled = false;
+
+ 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);
+ }
+ return 0;
} else {
- cli->sendMsg(ErrorCode::CommandOkay, "Volume mounted.", false);
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);
+ }
+
+ if (!rc) {
+ cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);
+ } else {
+ /*
+ * Failed
+ */
+ if (errno == ENODEV) {
+ rc = ResponseCode::OpFailedNoMedia;
+ } else if (errno == ENODATA) {
+ rc = ResponseCode::OpFailedMediaBlank;
+ } else if (errno == EIO) {
+ rc = ResponseCode::OpFailedMediaCorrupt;
+ } else if (errno == EBUSY) {
+ rc = ResponseCode::OpFailedVolBusy;
+ } else {
+ LOGW("Returning OperationFailed - no handler for errno %d", errno);
+ rc = ResponseCode::OperationFailed;
+ }
+ cli->sendMsg(rc, "volume operation failed", true);
}
return 0;
}
-CommandListener::UnmountVolumeCmd::UnmountVolumeCmd() :
- VoldCommand("unmount_volume") {
+CommandListener::ShareCmd::ShareCmd() :
+ VoldCommand("share") {
}
-int CommandListener::UnmountVolumeCmd::runCommand(SocketClient *cli,
+int CommandListener::ShareCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
- /* Synchronously unmount a volume */
- if (VolumeManager::Instance()->mountVolume(argv[1])) {
- cli->sendMsg(ErrorCode::OperationFailed, "Failed to unmount volume.", true);
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+ return 0;
+ }
+
+ VolumeManager *vm = VolumeManager::Instance();
+ int rc = 0;
+
+ if (!strcmp(argv[1], "status")) {
+ bool avail = false;
+
+ if (vm->shareAvailable(argv[2], &avail)) {
+ cli->sendMsg(
+ ResponseCode::OperationFailed, "Failed to determine share availability", true);
+ } else {
+ cli->sendMsg(ResponseCode::ShareStatusResult,
+ (avail ? "Share available" : "Share unavailable"), false);
+ }
} else {
- cli->sendMsg(ErrorCode::CommandOkay, "Volume unmounted.", false);
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown share cmd", false);
}
return 0;
}
-CommandListener::ShareVolumeCmd::ShareVolumeCmd() :
- VoldCommand("share_volume") {
+CommandListener::AsecCmd::AsecCmd() :
+ VoldCommand("asec") {
}
-int CommandListener::ShareVolumeCmd::runCommand(SocketClient *cli,
+int CommandListener::AsecCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
- VolumeManager *nm = VolumeManager::Instance();
- errno = ENOSYS;
- cli->sendMsg(ErrorCode::OperationFailed, "Failed to share volume", true);
- return 0;
-}
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+ return 0;
+ }
-CommandListener::UnshareVolumeCmd::UnshareVolumeCmd() :
- VoldCommand("unshare_volume") {
-}
+ VolumeManager *vm = VolumeManager::Instance();
+ int rc = 0;
-int CommandListener::UnshareVolumeCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- VolumeManager *nm = VolumeManager::Instance();
- errno = ENOSYS;
- cli->sendMsg(ErrorCode::OperationFailed, "Failed to unshare volume", true);
+ if (!strcmp(argv[1], "list")) {
+ 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);
+ } else if (!strcmp(argv[1], "create")) {
+ if (argc != 7) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid>", false);
+ return 0;
+ }
+
+ unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
+ if (vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]))) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Container creation failed", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Container created", false);
+ }
+ } else if (!strcmp(argv[1], "finalize")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
+ return 0;
+ }
+ if (vm->finalizeAsec(argv[2])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Container finalize failed", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Container finalized", false);
+ }
+ } else if (!strcmp(argv[1], "destroy")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id>", false);
+ return 0;
+ }
+ if (vm->destroyAsec(argv[2])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Container destroy failed", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Container destroyed", false);
+ }
+ } else if (!strcmp(argv[1], "mount")) {
+ if (argc != 5) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
+ return 0;
+ }
+
+ int rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
+
+ if (rc < 0) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Mount failed", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Mount succeeded", false);
+ }
+
+ } else if (!strcmp(argv[1], "unmount")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id>", false);
+ return 0;
+ }
+ if (vm->unmountAsec(argv[2])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Container unmount failed", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Container unmounted", false);
+ }
+ } else if (!strcmp(argv[1], "rename")) {
+ if (argc != 4) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: asec rename <old_id> <new_id>", false);
+ return 0;
+ }
+ if (vm->renameAsec(argv[2], argv[3])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Container rename failed", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Container renamed", false);
+ }
+ } else if (!strcmp(argv[1], "path")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
+ return 0;
+ }
+ char path[255];
+
+ if (vm->getAsecMountPath(argv[2], path, sizeof(path))) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to get path", true);
+ } else {
+ cli->sendMsg(ResponseCode::AsecPathResult, path, false);
+ }
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
+ }
+
return 0;
}
diff --git a/CommandListener.h b/CommandListener.h
index 9c770a2..e6fa805 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -27,38 +27,24 @@
private:
- class ListVolumesCmd : public VoldCommand {
+ class VolumeCmd : public VoldCommand {
public:
- ListVolumesCmd();
- virtual ~ListVolumesCmd() {}
+ VolumeCmd();
+ virtual ~VolumeCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class MountVolumeCmd : public VoldCommand {
+ class ShareCmd : public VoldCommand {
public:
- MountVolumeCmd();
- virtual ~MountVolumeCmd() {}
+ ShareCmd();
+ virtual ~ShareCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class UnmountVolumeCmd : public VoldCommand {
+ class AsecCmd : public VoldCommand {
public:
- UnmountVolumeCmd();
- virtual ~UnmountVolumeCmd() {}
- int runCommand(SocketClient *c, int argc, char ** argv);
- };
-
- class ShareVolumeCmd : public VoldCommand {
- public:
- ShareVolumeCmd();
- virtual ~ShareVolumeCmd() {}
- int runCommand(SocketClient *c, int argc, char ** argv);
- };
-
- class UnshareVolumeCmd : public VoldCommand {
- public:
- UnshareVolumeCmd();
- virtual ~UnshareVolumeCmd() {}
+ AsecCmd();
+ virtual ~AsecCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
diff --git a/Devmapper.cpp b/Devmapper.cpp
new file mode 100644
index 0000000..9dd8ef3
--- /dev/null
+++ b/Devmapper.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include "Devmapper.h"
+
+void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize,
+ const char *name, unsigned flags) {
+ memset(io, 0, dataSize);
+ io->data_size = dataSize;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags;
+ strncpy(io->name, name, sizeof(io->name));
+}
+
+int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
+ char *buffer = (char *) malloc(4096);
+ if (!buffer) {
+ LOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ LOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+
+ ioctlInit(io, 4096, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ if (errno != ENXIO) {
+ LOGE("DM_DEV_STATUS ioctl failed for lookup (%s)", strerror(errno));
+ }
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ free(buffer);
+ LOGD("Newly created devmapper instance minor = %d\n", minor);
+ snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
+ return 0;
+}
+
+int Devmapper::create(const char *name, const char *loopFile, const char *key,
+ unsigned int numSectors, char *ubuffer, size_t len) {
+ char *buffer = (char *) malloc(4096);
+ if (!buffer) {
+ LOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ LOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+
+ // Create the DM device
+ ioctlInit(io, 4096, name, 0);
+
+ if (ioctl(fd, DM_DEV_CREATE, io)) {
+ LOGE("Error creating device mapping (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ // Set the legacy geometry
+ ioctlInit(io, 4096, name, 0);
+
+ char *geoParams = buffer + sizeof(struct dm_ioctl);
+ // bps=512 spc=8 res=32 nft=2 sec=8190 mid=0xf0 spt=63 hds=64 hid=0 bspf=8 rdcl=2 infs=1 bkbs=2
+ strcpy(geoParams, "0 64 63 0");
+ geoParams += strlen(geoParams) + 1;
+ geoParams = (char *) _align(geoParams, 8);
+ if (ioctl(fd, DM_DEV_SET_GEOMETRY, io)) {
+ LOGE("Error setting device geometry (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ // Retrieve the device number we were allocated
+ ioctlInit(io, 4096, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ LOGE("Error retrieving devmapper status (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ LOGD("Newly created devmapper instance minor = %d\n", minor);
+ snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
+
+ // Load the table
+ struct dm_target_spec *tgt;
+ tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+ ioctlInit(io, 4096, name, DM_STATUS_TABLE_FLAG);
+ io->target_count = 1;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = numSectors;
+ strcpy(tgt->target_type, "crypt");
+
+ char *cryptParams = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ sprintf(cryptParams, "twofish %s 0 %s 0", key, loopFile);
+ cryptParams += strlen(cryptParams) + 1;
+ cryptParams = (char *) _align(cryptParams, 8);
+ tgt->next = cryptParams - buffer;
+
+ if (ioctl(fd, DM_TABLE_LOAD, io)) {
+ LOGE("Error loading mapping table (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ // Resume the new table
+ ioctlInit(io, 4096, name, 0);
+
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ LOGE("Error Resuming (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+int Devmapper::destroy(const char *name) {
+ char *buffer = (char *) malloc(4096);
+ if (!buffer) {
+ LOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ LOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+
+ // Create the DM device
+ ioctlInit(io, 4096, name, 0);
+
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ if (errno != ENXIO) {
+ LOGE("Error destroying device mapping (%s)", strerror(errno));
+ }
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ free(buffer);
+ close(fd);
+ return 0;
+}
+
+void *Devmapper::_align(void *ptr, unsigned int a)
+{
+ register unsigned long agn = --a;
+
+ return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
diff --git a/Devmapper.h b/Devmapper.h
new file mode 100644
index 0000000..52e2bed
--- /dev/null
+++ b/Devmapper.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _DEVMAPPER_H
+#define _DEVMAPPER_H
+
+#include <unistd.h>
+#include <linux/dm-ioctl.h>
+
+class Devmapper {
+public:
+ static int create(const char *name, const char *loopFile, const char *key,
+ unsigned int numSectors, char *buffer, size_t len);
+ static int destroy(const char *name);
+ static int lookupActive(const char *name, char *buffer, size_t len);
+private:
+ static void *_align(void *ptr, unsigned int a);
+ static void ioctlInit(struct dm_ioctl *io, size_t data_size,
+ const char *name, unsigned flags);
+};
+
+#endif
diff --git a/DirectVolume.cpp b/DirectVolume.cpp
index cd12cf4..68a6fcd 100644
--- a/DirectVolume.cpp
+++ b/DirectVolume.cpp
@@ -19,20 +19,33 @@
#include <string.h>
#include <errno.h>
-#define LOG_TAG "Vold"
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "DirectVolume"
#include <cutils/log.h>
#include <sysutils/NetlinkEvent.h>
#include "DirectVolume.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
-DirectVolume::DirectVolume(const char *label, const char *mount_point, int partIdx) :
- Volume(label, mount_point) {
+// #define PARTITION_DEBUG
+
+DirectVolume::DirectVolume(VolumeManager *vm, const char *label,
+ const char *mount_point, int partIdx) :
+ Volume(vm, label, mount_point) {
mPartIdx = partIdx;
-
+
mPaths = new PathCollection();
for (int i = 0; i < MAX_PARTITIONS; i++)
mPartMinors[i] = -1;
+ mPendingPartMap = 0;
+ mDiskMajor = -1;
+ mDiskMinor = -1;
+ mDiskNumParts = 0;
+
+ setState(Volume::State_NoMedia);
}
DirectVolume::~DirectVolume() {
@@ -48,6 +61,18 @@
return 0;
}
+dev_t DirectVolume::getDiskDevice() {
+ return MKDEV(mDiskMajor, mDiskMinor);
+}
+
+void DirectVolume::handleVolumeShared() {
+ setState(Volume::State_Shared);
+}
+
+void DirectVolume::handleVolumeUnshared() {
+ setState(Volume::State_Idle);
+}
+
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
const char *dp = evt->findParam("DEVPATH");
@@ -58,20 +83,37 @@
int action = evt->getAction();
const char *devtype = evt->findParam("DEVTYPE");
- if (!strcmp(devtype, "disk")) {
- if (action == NetlinkEvent::NlActionAdd)
+ if (action == NetlinkEvent::NlActionAdd) {
+ int major = atoi(evt->findParam("MAJOR"));
+ int minor = atoi(evt->findParam("MINOR"));
+ char nodepath[255];
+
+ snprintf(nodepath,
+ sizeof(nodepath), "/dev/block/vold/%d:%d",
+ major, minor);
+ if (createDeviceNode(nodepath, major, minor)) {
+ LOGE("Error making device node '%s' (%s)", nodepath,
+ strerror(errno));
+ }
+ if (!strcmp(devtype, "disk")) {
handleDiskAdded(dp, evt);
- else if (action == NetlinkEvent::NlActionRemove)
- handleDiskRemoved(dp, evt);
- else
- LOGD("Ignoring non add/remove event");
- } else {
- if (action == NetlinkEvent::NlActionAdd)
+ } else {
handlePartitionAdded(dp, evt);
- else if (action == NetlinkEvent::NlActionRemove)
+ }
+ } else if (action == NetlinkEvent::NlActionRemove) {
+ if (!strcmp(devtype, "disk")) {
+ handleDiskRemoved(dp, evt);
+ } else {
handlePartitionRemoved(dp, evt);
- else
- LOGD("Ignoring non add/remove event");
+ }
+ } else if (action == NetlinkEvent::NlActionChange) {
+ if (!strcmp(devtype, "disk")) {
+ handleDiskChanged(dp, evt);
+ } else {
+ handlePartitionChanged(dp, evt);
+ }
+ } else {
+ LOGW("Ignoring non add/remove/change event");
}
return 0;
@@ -83,8 +125,17 @@
void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
mDiskMajor = atoi(evt->findParam("MAJOR"));
- mDiskMinor = atoi(evt->findParam("MAJOR"));
- mDiskNumParts = atoi(evt->findParam("NPARTS"));
+ mDiskMinor = atoi(evt->findParam("MINOR"));
+
+ const char *tmp = evt->findParam("NPARTS");
+ if (tmp) {
+ mDiskNumParts = atoi(tmp);
+ } else {
+ LOGW("Kernel block uevent missing 'NPARTS'");
+ mDiskNumParts = 1;
+ }
+
+ char msg[255];
int partmask = 0;
int i;
@@ -94,72 +145,165 @@
mPendingPartMap = partmask;
if (mDiskNumParts == 0) {
+#ifdef PARTITION_DEBUG
LOGD("Dv::diskIns - No partitions - good to go son!");
+#endif
setState(Volume::State_Idle);
} else {
+#ifdef PARTITION_DEBUG
LOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
mDiskNumParts, mPendingPartMap);
+#endif
setState(Volume::State_Pending);
}
+
+ snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
+ getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
+ mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
+ msg, false);
}
void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
- int part_num = atoi(evt->findParam("PARTN"));
+
+ int part_num;
+
+ const char *tmp = evt->findParam("PARTN");
+
+ if (tmp) {
+ part_num = atoi(tmp);
+ } else {
+ LOGW("Kernel block uevent missing 'PARTN'");
+ part_num = 1;
+ }
if (major != mDiskMajor) {
LOGE("Partition '%s' has a different major than its disk!", devpath);
return;
}
+#ifdef PARTITION_DEBUG
+ LOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
+#endif
mPartMinors[part_num -1] = minor;
mPendingPartMap &= ~(1 << part_num);
if (!mPendingPartMap) {
+#ifdef PARTITION_DEBUG
LOGD("Dv:partAdd: Got all partitions - ready to rock!");
+#endif
setState(Volume::State_Idle);
} else {
+#ifdef PARTITION_DEBUG
LOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
+#endif
}
}
+void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
+ int major = atoi(evt->findParam("MAJOR"));
+ int minor = atoi(evt->findParam("MINOR"));
+
+ if ((major != mDiskMajor) || (minor != mDiskMinor)) {
+ return;
+ }
+
+ LOGI("Volume %s disk has changed", getLabel());
+ const char *tmp = evt->findParam("NPARTS");
+ if (tmp) {
+ mDiskNumParts = atoi(tmp);
+ } else {
+ LOGW("Kernel block uevent missing 'NPARTS'");
+ mDiskNumParts = 1;
+ }
+
+ int partmask = 0;
+ int i;
+ for (i = 1; i <= mDiskNumParts; i++) {
+ partmask |= (1 << i);
+ }
+ mPendingPartMap = partmask;
+
+ if (mDiskNumParts == 0) {
+ setState(Volume::State_Idle);
+ } else {
+ setState(Volume::State_Pending);
+ }
+
+}
+
+void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
+}
+
void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
+ int major = atoi(evt->findParam("MAJOR"));
+ int minor = atoi(evt->findParam("MINOR"));
+ char msg[255];
+
+ LOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
+ snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
+ getLabel(), getMountpoint(), major, minor);
+ mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
+ msg, false);
+ setState(Volume::State_NoMedia);
}
void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
+ int major = atoi(evt->findParam("MAJOR"));
+ int minor = atoi(evt->findParam("MINOR"));
+ char msg[255];
+
+ LOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
+
+ /*
+ * The framework doesn't need to get notified of
+ * partition removal unless it's mounted. Otherwise
+ * the removal notification will be sent on the Disk
+ * itself
+ */
+ if (getState() != Volume::State_Mounted) {
+ return;
+ }
+
+ if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
+ /*
+ * Yikes, our mounted partition is going away!
+ */
+
+ snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
+ getLabel(), getMountpoint(), major, minor);
+ mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
+ msg, false);
+ if (Volume::unmountVol()) {
+ LOGE("Failed to unmount volume on bad removal (%s)",
+ strerror(errno));
+ // XXX: At this point we're screwed for now
+ } else {
+ LOGD("Crisis averted");
+ }
+ }
}
/*
- * Called from Volume to determine the major/minor numbers
- * to be used for mounting
+ * Called from base to get a list of devicenodes for mounting
*/
-int DirectVolume::prepareToMount(int *major, int *minor) {
- *major = mDiskMajor;
+int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
if (mPartIdx == -1) {
- /* No specific partition specified */
-
+ // If the disk has no partitions, try the disk itself
if (!mDiskNumParts) {
- *minor = mDiskMinor;
- return 0;
+ devs[0] = MKDEV(mDiskMajor, mDiskMinor);
+ return 1;
}
- /*
- * XXX: Use first partition for now.
- * The right thing to do would be to choose
- * this based on the partition type.
- *
- */
-
- *minor = mPartMinors[0];
- return 0;
+ int i;
+ for (i = 0; i < mDiskNumParts; i++) {
+ if (i == max)
+ break;
+ devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
+ }
+ return mDiskNumParts;
}
-
- if (mPartIdx - 1 > mDiskNumParts) {
- errno = EINVAL;
- return -1;
- }
-
- *minor = mPartMinors[mPartIdx-1];
- return 0;
+ devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
+ return 1;
}
diff --git a/DirectVolume.h b/DirectVolume.h
index a66d27a..2a78236 100644
--- a/DirectVolume.h
+++ b/DirectVolume.h
@@ -38,20 +38,28 @@
unsigned char mPendingPartMap;
public:
- DirectVolume(const char *label, const char *mount_point, int partIdx);
+ DirectVolume(VolumeManager *vm, const char *label, const char *mount_point, int partIdx);
virtual ~DirectVolume();
int addPath(const char *path);
int handleBlockEvent(NetlinkEvent *evt);
+ dev_t getDiskDevice();
+ void handleVolumeShared();
+ void handleVolumeUnshared();
+
protected:
- int prepareToMount(int *major, int *minor);
+ int getDeviceNodes(dev_t *devs, int max);
private:
void handleDiskAdded(const char *devpath, NetlinkEvent *evt);
void handleDiskRemoved(const char *devpath, NetlinkEvent *evt);
+ void handleDiskChanged(const char *devpath, NetlinkEvent *evt);
void handlePartitionAdded(const char *devpath, NetlinkEvent *evt);
void handlePartitionRemoved(const char *devpath, NetlinkEvent *evt);
+ void handlePartitionChanged(const char *devpath, NetlinkEvent *evt);
+
+ int doMountVfat(const char *deviceNode, const char *mountPoint);
};
diff --git a/ErrorCode.h b/ErrorCode.h
deleted file mode 100644
index 328b2b1..0000000
--- a/ErrorCode.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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 _ERRORCODE_H
-#define _ERRORCODE_H
-
-class ErrorCode {
-public:
- // 100 series - Requestion action was initiated; expect another reply
- // before proceeding with a new command.
- static const int ActionInitiated = 100;
-
- static const int VolumeListResult = 110;
-
- // 200 series - Requested action has been successfully completed
- static const int CommandOkay = 200;
-
- // 400 series - The command was accepted but the requested action
- // did not take place.
- static const int OperationFailed = 400;
-
- // 500 series - The command was not accepted and the requested
- // action did not take place.
- static const int CommandSyntaxError = 500;
- static const int CommandParameterError = 501;
-
- // 600 series - Unsolicited broadcasts
- static const int UnsolicitedInformational = 600;
-};
-#endif
diff --git a/Fat.cpp b/Fat.cpp
new file mode 100644
index 0000000..f85bd8e
--- /dev/null
+++ b/Fat.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "Fat.h"
+
+static char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";
+static char MKDOSFS_PATH[] = "/system/bin/newfs_msdos";
+extern "C" int logwrap(int argc, const char **argv, int background);
+extern "C" int mount(const char *, const char *, const char *, unsigned long, const void *);
+
+int Fat::check(const char *fsPath) {
+ bool rw = true;
+ if (access(FSCK_MSDOS_PATH, X_OK)) {
+ LOGW("Skipping fs checks\n");
+ return 0;
+ }
+
+ int pass = 1;
+ int rc = 0;
+ do {
+ const char *args[5];
+ args[0] = FSCK_MSDOS_PATH;
+ args[1] = "-p";
+ args[2] = "-f";
+ args[3] = fsPath;
+ args[4] = NULL;
+
+ rc = logwrap(4, args, 1);
+
+ switch(rc) {
+ case 0:
+ LOGI("Filesystem check completed OK");
+ return 0;
+
+ case 2:
+ LOGE("Filesystem check failed (not a FAT filesystem)");
+ errno = ENODATA;
+ return -1;
+
+ case 4:
+ if (pass++ <= 3) {
+ LOGW("Filesystem modified - rechecking (pass %d)",
+ pass);
+ continue;
+ }
+ LOGE("Failing check after too many rechecks");
+ errno = EIO;
+ return -1;
+
+ default:
+ LOGE("Filesystem check failed (unknown exit code %d)", rc);
+ errno = EIO;
+ return -1;
+ }
+ } while (0);
+
+ return 0;
+}
+
+int Fat::doMount(const char *fsPath, const char *mountPoint,
+ bool ro, bool remount, int ownerUid, int ownerGid,
+ int permMask, bool createLost) {
+ int rc;
+ unsigned long flags;
+ char mountData[255];
+
+ 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.
+ *
+ * TODO: Remove this code once we have a drop box in system_server.
+ */
+ char value[PROPERTY_VALUE_MAX];
+ property_get("persist.sampling_profiler", value, "");
+ if (value[0] == '1') {
+ LOGW("The SD card is world-writable because the"
+ " 'persist.sampling_profiler' system property is set to '1'.");
+ permMask = 0;
+ }
+
+ sprintf(mountData,
+ "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",
+ ownerUid, ownerGid, permMask, permMask);
+
+ rc = mount(fsPath, mountPoint, "vfat", flags, mountData);
+
+ if (rc && errno == EROFS) {
+ LOGE("%s appears to be a read only filesystem - retrying mount RO", fsPath);
+ flags |= MS_RDONLY;
+ rc = mount(fsPath, mountPoint, "vfat", flags, mountData);
+ }
+
+ if (rc == 0 && createLost) {
+ char *lost_path;
+ asprintf(&lost_path, "%s/LOST.DIR", mountPoint);
+ if (access(lost_path, F_OK)) {
+ /*
+ * Create a LOST.DIR in the root so we have somewhere to put
+ * lost cluster chains (fsck_msdos doesn't currently do this)
+ */
+ if (mkdir(lost_path, 0755)) {
+ LOGE("Unable to create LOST.DIR (%s)", strerror(errno));
+ }
+ }
+ free(lost_path);
+ }
+
+ return rc;
+}
+
+int Fat::format(const char *fsPath) {
+ unsigned int nr_sec;
+ int fd;
+
+ if ((fd = open(fsPath, O_RDWR)) < 0) {
+ LOGE("Error opening disk file (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
+ LOGE("Unable to get device size (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ const char *args[9];
+ int rc;
+ args[0] = MKDOSFS_PATH;
+ args[1] = "-F";
+ args[2] = "32";
+ args[3] = "-O";
+ args[4] = "android";
+ args[5] = "-c";
+ args[6] = "8";
+ args[7] = fsPath;
+ args[8] = NULL;
+ rc = logwrap(9, args, 1);
+
+ if (rc == 0) {
+ LOGI("Filesystem formatted OK");
+ return 0;
+ } else {
+ LOGE("Format failed (unknown exit code %d)", rc);
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
diff --git a/Fat.h b/Fat.h
new file mode 100644
index 0000000..ab16a7f
--- /dev/null
+++ b/Fat.h
@@ -0,0 +1,31 @@
+/*
+ * 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 _FAT_H
+#define _FAT_H
+
+#include <unistd.h>
+
+class Fat {
+public:
+ static int check(const char *fsPath);
+ static int doMount(const char *fsPath, const char *mountPoint, bool ro,
+ bool remount, int ownerUid, int ownerGid, int permMask,
+ bool createLost);
+ static int format(const char *fsPath);
+};
+
+#endif
diff --git a/Loop.cpp b/Loop.cpp
new file mode 100644
index 0000000..a54d73e
--- /dev/null
+++ b/Loop.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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>
+
+#include <sys/types.h>
+#include <sys/stat.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) {
+ if (errno != ENOENT) {
+ 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::create(const char *loopFile, char *loopDeviceBuffer, size_t len) {
+ int i;
+ int fd;
+ char filename[256];
+
+ for (i = 0; i < LOOP_MAX; i++) {
+ struct loop_info li;
+ int rc;
+
+ sprintf(filename, "/dev/block/loop%d", i);
+
+ /*
+ * The kernel starts us off with 8 loop nodes, but more
+ * are created on-demand if needed.
+ */
+ mode_t mode = 0660 | S_IFBLK;
+ unsigned int dev = (0xff & i) | ((i << 12) & 0xfff00000) | (7 << 8);
+ if (mknod(filename, mode, dev) < 0) {
+ if (errno != EEXIST) {
+ LOGE("Error creating loop device node (%s)", strerror(errno));
+ return -1;
+ }
+ }
+
+ 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);
+ if (rc < 0 && errno == ENXIO)
+ break;
+
+ close(fd);
+
+ 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(loopDeviceBuffer, filename, len -1);
+
+ int file_fd;
+
+ 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, unsigned int numSectors) {
+ int fd;
+
+ LOGD("Creating ASEC image file %s (%u sectors)", file, numSectors);
+
+ if ((fd = creat(file, 0600)) < 0) {
+ LOGE("Error creating imagefile (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (ftruncate(fd, numSectors * 512) < 0) {
+ LOGE("Error truncating imagefile (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
diff --git a/Loop.h b/Loop.h
new file mode 100644
index 0000000..f06f91d
--- /dev/null
+++ b/Loop.h
@@ -0,0 +1,34 @@
+/*
+ * 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 = 4096;
+public:
+ static int lookupActive(const char *loopFile, char *buffer, size_t len);
+ static int create(const char *loopFile, char *loopDeviceBuffer, size_t len);
+ static int destroyByDevice(const char *loopDevice);
+ static int destroyByFile(const char *loopFile);
+ static int createImageFile(const char *file, unsigned int numSectors);
+};
+
+#endif
diff --git a/NetlinkHandler.cpp b/NetlinkHandler.cpp
index 19ec932..8a3ebcb 100644
--- a/NetlinkHandler.cpp
+++ b/NetlinkHandler.cpp
@@ -52,9 +52,9 @@
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt);
+ } else if (!strcmp(subsys, "switch")) {
+ vm->handleSwitchEvent(evt);
} else if (!strcmp(subsys, "battery")) {
} else if (!strcmp(subsys, "power_supply")) {
- } else {
- LOGE("Dropping %s netlink event", subsys);
}
}
diff --git a/ProcessKiller.c b/ProcessKiller.c
new file mode 100644
index 0000000..ad36731
--- /dev/null
+++ b/ProcessKiller.c
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+/*
+** mountd process killer
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#define LOG_TAG "ProcessKiller"
+#include <cutils/log.h>
+
+#define PATH_MAX 4096
+
+static int ReadSymLink(const char* path, char* link)
+{
+ struct stat s;
+ int length;
+
+ if (lstat(path, &s) < 0)
+ return 0;
+ if ((s.st_mode & S_IFMT) != S_IFLNK)
+ return 0;
+
+ // we have a symlink
+ length = readlink(path, link, PATH_MAX - 1);
+ if (length <= 0)
+ return 0;
+ link[length] = 0;
+ return 1;
+}
+
+static int PathMatchesMountPoint(const char* path, const char* mountPoint)
+{
+ int length = strlen(mountPoint);
+ if (length > 1 && strncmp(path, mountPoint, length) == 0)
+ {
+ // we need to do extra checking if mountPoint does not end in a '/'
+ if (mountPoint[length - 1] == '/')
+ return 1;
+ // if mountPoint does not have a trailing slash, we need to make sure
+ // there is one in the path to avoid partial matches.
+ return (path[length] == 0 || path[length] == '/');
+ }
+
+ return 0;
+}
+
+static void GetProcessName(int pid, char buffer[PATH_MAX])
+{
+ int fd;
+ sprintf(buffer, "/proc/%d/cmdline", pid);
+ fd = open(buffer, O_RDONLY);
+ if (fd < 0) {
+ strcpy(buffer, "???");
+ } else {
+ int length = read(fd, buffer, PATH_MAX - 1);
+ buffer[length] = 0;
+ close(fd);
+ }
+}
+
+static int CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
+{
+ DIR* dir;
+ struct dirent* de;
+ int fileOpen = 0;
+ char path[PATH_MAX];
+ char link[PATH_MAX];
+ int parent_length;
+
+ // compute path to process's directory of open files
+ sprintf(path, "/proc/%d/fd", pid);
+ dir = opendir(path);
+ if (!dir)
+ return 0;
+
+ // remember length of the path
+ parent_length = strlen(path);
+ // append a trailing '/'
+ path[parent_length++] = '/';
+
+ while ((de = readdir(dir)) != 0 && !fileOpen) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ // append the file name, after truncating to parent directory
+ path[parent_length] = 0;
+ strcat(path, de->d_name);
+
+ if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
+ {
+ char name[PATH_MAX];
+ GetProcessName(pid, name);
+ LOGE("Process %s (%d) has open file %s", name, pid, link);
+ fileOpen = 1;
+ }
+ }
+
+ closedir(dir);
+ return fileOpen;
+}
+
+static int CheckFileMaps(int pid, const char* mountPoint)
+{
+ FILE* file;
+ char buffer[PATH_MAX + 100];
+ int mapOpen = 0;
+
+ sprintf(buffer, "/proc/%d/maps", pid);
+ file = fopen(buffer, "r");
+ if (!file)
+ return 0;
+
+ while (!mapOpen && fgets(buffer, sizeof(buffer), file))
+ {
+ // skip to the path
+ const char* path = strchr(buffer, '/');
+ if (path && PathMatchesMountPoint(path, mountPoint))
+ {
+ char name[PATH_MAX];
+ GetProcessName(pid, name);
+ LOGE("process %s (%d) has open file map for %s", name, pid, path);
+ mapOpen = 1;
+ }
+ }
+
+ fclose(file);
+ return mapOpen;
+}
+
+static int CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
+{
+ char path[PATH_MAX];
+ char link[PATH_MAX];
+
+ sprintf(path, "/proc/%d/%s", pid, name);
+ if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
+ {
+ char name[PATH_MAX];
+ GetProcessName(pid, name);
+ LOGW("Process %s (%d) has %s in %s", name, pid, message, mountPoint);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static int get_pid(const char* s)
+{
+ int result = 0;
+ while (*s) {
+ if (!isdigit(*s)) return -1;
+ result = 10 * result + (*s++ - '0');
+ }
+ return result;
+}
+
+// hunt down and kill processes that have files open on the given mount point
+void KillProcessesWithOpenFiles(const char* mountPoint, int sigkill, int *excluded, int num_excluded)
+{
+ DIR* dir;
+ struct dirent* de;
+
+ dir = opendir("/proc");
+ if (!dir) return;
+
+ while ((de = readdir(dir)) != 0)
+ {
+ int killed = 0;
+ // does the name look like a process ID?
+ int pid = get_pid(de->d_name);
+ if (pid == -1) continue;
+
+ if (CheckFileDescriptorSymLinks(pid, mountPoint) // check for open files
+ || CheckFileMaps(pid, mountPoint) // check for mmap()
+ || CheckSymLink(pid, mountPoint, "cwd", "working directory") // check working directory
+ || CheckSymLink(pid, mountPoint, "root", "chroot") // check for chroot()
+ || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path
+ )
+ {
+ int i;
+ int hit = 0;
+
+ for (i = 0; i < num_excluded; i++) {
+ if (pid == excluded[i]) {
+ LOGE("I just need a little more TIME captain!");
+ hit = 1;
+ break;
+ }
+ }
+
+ if (!hit) {
+ if (!sigkill) {
+ kill(pid, SIGTERM);
+ } else {
+ // Log this as an error since the app should
+ // have released it's resources long before
+ // this point.
+
+ LOGE("Sending SIGKILL to process %d", pid);
+ kill(pid, SIGKILL);
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+}
diff --git a/ResponseCode.h b/ResponseCode.h
new file mode 100644
index 0000000..9d775f9
--- /dev/null
+++ b/ResponseCode.h
@@ -0,0 +1,62 @@
+/*
+ * 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 _RESPONSECODE_H
+#define _RESPONSECODE_H
+
+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 VolumeListResult = 110;
+ static const int AsecListResult = 111;
+
+ // 200 series - Requested action has been successfully completed
+ static const int CommandOkay = 200;
+ static const int ShareStatusResult = 210;
+ static const int AsecPathResult = 211;
+ static const int ShareEnabledResult = 212;
+
+ // 400 series - The command was accepted but the requested action
+ // did not take place.
+ static const int OperationFailed = 400;
+ static const int OpFailedNoMedia = 401;
+ static const int OpFailedMediaBlank = 402;
+ static const int OpFailedMediaCorrupt = 403;
+ static const int OpFailedVolNotMounted = 404;
+ static const int OpFailedVolBusy = 405;
+
+ // 500 series - The command was not accepted and the requested
+ // action did not take place.
+ static const int CommandSyntaxError = 500;
+ static const int CommandParameterError = 501;
+
+ // 600 series - Unsolicited broadcasts
+ static const int UnsolicitedInformational = 600;
+ static const int VolumeStateChange = 605;
+ static const int VolumeMountFailedBlank = 610;
+ static const int VolumeMountFailedDamaged = 611;
+ static const int VolumeMountFailedNoMedia = 612;
+
+ static const int ShareAvailabilityChange = 620;
+
+ static const int VolumeDiskInserted = 630;
+ static const int VolumeDiskRemoved = 631;
+ static const int VolumeBadRemoval = 632;
+};
+#endif
diff --git a/Volume.cpp b/Volume.cpp
index bdd0ada..6a81eff 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -24,21 +24,58 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
+#include <sys/mount.h>
+
+#include <linux/kdev_t.h>
+
+#include <cutils/properties.h>
+
+#include "diskmbr.h"
#define LOG_TAG "Vold"
#include <cutils/log.h>
#include "Volume.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+#include "Fat.h"
-extern "C" int logwrap(int argc, const char **argv, int background);
+extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int);
+extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
+extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
-static char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";
+static const char *stateToStr(int state) {
+ if (state == Volume::State_Init)
+ return "Initializing";
+ else if (state == Volume::State_NoMedia)
+ return "No-Media";
+ else if (state == Volume::State_Idle)
+ return "Idle-Unmounted";
+ else if (state == Volume::State_Pending)
+ return "Pending";
+ else if (state == Volume::State_Mounted)
+ return "Mounted";
+ else if (state == Volume::State_Unmounting)
+ return "Unmounting";
+ else if (state == Volume::State_Checking)
+ return "Checking";
+ else if (state == Volume::State_Formatting)
+ return "Formatting";
+ else if (state == Volume::State_Shared)
+ return "Shared-Unmounted";
+ else if (state == Volume::State_SharedMnt)
+ return "Shared-Mounted";
+ else
+ return "Unknown-Error";
+}
-Volume::Volume(const char *label, const char *mount_point) {
+Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
+ mVm = vm;
mLabel = strdup(label);
mMountpoint = strdup(mount_point);
mState = Volume::State_Init;
+ mCurrentlyMountedKdev = -1;
}
Volume::~Volume() {
@@ -46,14 +83,41 @@
free(mMountpoint);
}
+dev_t Volume::getDiskDevice() {
+ return MKDEV(0, 0);
+};
+
+void Volume::handleVolumeShared() {
+}
+
+void Volume::handleVolumeUnshared() {
+}
+
int Volume::handleBlockEvent(NetlinkEvent *evt) {
errno = ENOSYS;
return -1;
}
void Volume::setState(int state) {
- LOGD("Volume %s state changing %d -> %d", mLabel, mState, state);
+ char msg[255];
+ int oldState = mState;
+
+ if (oldState == state) {
+ LOGW("Duplicate state (%d)\n", state);
+ return;
+ }
+
mState = state;
+
+ LOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
+ oldState, stateToStr(oldState), mState, stateToStr(mState));
+ snprintf(msg, sizeof(msg),
+ "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
+ getMountpoint(), oldState, stateToStr(oldState), mState,
+ stateToStr(mState));
+
+ mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
+ msg, false);
}
int Volume::createDeviceNode(const char *path, int major, int minor) {
@@ -67,91 +131,236 @@
return 0;
}
-int Volume::mount() {
- char nodepath[255];
- int major = -1, minor = -1;
+int Volume::formatVol() {
- if (prepareToMount(&major, &minor)) {
- LOGE("Volume failed to prepare: %s", strerror(errno));
+ if (getState() == Volume::State_NoMedia) {
+ errno = ENODEV;
+ return -1;
+ } else if (getState() != Volume::State_Idle) {
+ errno = EBUSY;
return -1;
}
- sprintf(nodepath, "/dev/block/vold/%d:%d", major, minor);
-
- LOGD("nodepath = %s\n", nodepath);
-
- /* Create device nodes */
- if (createDeviceNode(nodepath, major, minor)) {
- LOGE("Error making device nodes for '%s' (%s)", nodepath,
- strerror(errno));
- // XXX: cleanup will be needed eventually
+ if (isMountpointMounted(getMountpoint())) {
+ LOGW("Volume is idle but appears to be mounted - fixing");
+ setState(Volume::State_Mounted);
+ // mCurrentlyMountedKdev = XXX
+ errno = EBUSY;
return -1;
}
- /* Run disk checker */
- if (checkFilesystem(nodepath)) {
- LOGE("Error checking filesystem (%s)", strerror(errno));
- setState(Volume::State_Idle);
- return -1;
+ char devicePath[255];
+ dev_t diskNode = getDiskDevice();
+ dev_t partNode = MKDEV(MAJOR(diskNode), 1); // XXX: Hmmm
+
+ sprintf(devicePath, "/dev/block/vold/%d:%d",
+ MAJOR(diskNode), MINOR(diskNode));
+
+ LOGI("Formatting volume %s (%s)", getLabel(), devicePath);
+
+ if (initializeMbr(devicePath)) {
+ LOGE("Failed to initialize MBR (%s)", strerror(errno));
+ goto err;
}
-
+ sprintf(devicePath, "/dev/block/vold/%d:%d",
+ MAJOR(partNode), MINOR(partNode));
- setState(Volume::State_Idle);
+ if (Fat::format(devicePath)) {
+ LOGE("Failed to format (%s)", strerror(errno));
+ goto err;
+ }
+
return 0;
+err:
+ return -1;
}
-int Volume::checkFilesystem(const char *nodepath) {
+bool Volume::isMountpointMounted(const char *path) {
+ char device[256];
+ char mount_path[256];
+ char rest[256];
+ FILE *fp;
+ char line[1024];
- bool rw = true;
- if (access(FSCK_MSDOS_PATH, X_OK)) {
- LOGW("Skipping fs checks\n");
+ 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, path)) {
+ fclose(fp);
+ return true;
+ }
+
+ }
+
+ fclose(fp);
+ return false;
+}
+
+int Volume::mountVol() {
+ dev_t deviceNodes[4];
+ int n, i, rc = 0;
+ char errmsg[255];
+
+ if (getState() == Volume::State_NoMedia) {
+ snprintf(errmsg, sizeof(errmsg),
+ "Volume %s %s mount failed - no media",
+ getLabel(), getMountpoint());
+ mVm->getBroadcaster()->sendBroadcast(
+ ResponseCode::VolumeMountFailedNoMedia,
+ errmsg, false);
+ errno = ENODEV;
+ return -1;
+ } else if (getState() != Volume::State_Idle) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ if (isMountpointMounted(getMountpoint())) {
+ LOGW("Volume is idle but appears to be mounted - fixing");
+ setState(Volume::State_Mounted);
+ // mCurrentlyMountedKdev = XXX
return 0;
}
- setState(Volume::State_Checking);
- int pass = 1;
- int rc = 0;
- do {
- const char *args[5];
- args[0] = FSCK_MSDOS_PATH;
- args[1] = "-p";
- args[2] = "-f";
- args[3] = nodepath;
- args[4] = NULL;
+ n = getDeviceNodes((dev_t *) &deviceNodes, 4);
+ if (!n) {
+ LOGE("Failed to get device nodes (%s)\n", strerror(errno));
+ return -1;
+ }
- rc = logwrap(4, args, 1);
+ for (i = 0; i < n; i++) {
+ char devicePath[255];
- switch(rc) {
- case 0:
- LOGI("Filesystem check completed OK");
- return 0;
+ sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
+ MINOR(deviceNodes[i]));
- case 2:
- LOGE("Filesystem check failed (not a FAT filesystem)");
- errno = ENODATA;
- return -1;
+ LOGI("%s being considered for volume %s\n", devicePath, getLabel());
- case 4:
- if (pass++ <= 3) {
- LOGW("Filesystem modified - rechecking (pass %d)",
- pass);
+ errno = 0;
+ setState(Volume::State_Checking);
+
+ if ((rc = Fat::check(devicePath))) {
+ if (errno == ENODATA) {
+ LOGW("%s does not contain a FAT filesystem\n", devicePath);
continue;
}
- LOGE("Failing check after too many rechecks");
errno = EIO;
- return -1;
-
- default:
- LOGE("Filesystem check failed (unknown exit code %d)", rc);
- errno = EIO;
+ /* Badness - abort the mount */
+ LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
+ setState(Volume::State_Idle);
return -1;
}
- } while (0);
- return 0;
+ errno = 0;
+ if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false,
+ 1000, 1015, 0702, true))) {
+ LOGI("%s sucessfully mounted for volume %s\n", devicePath, getLabel());
+ setState(Volume::State_Mounted);
+ mCurrentlyMountedKdev = deviceNodes[i];
+ return 0;
+ }
+
+ LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
+ }
+
+ LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
+ setState(Volume::State_Idle);
+
+ return -1;
}
-int Volume::unmount() {
+int Volume::unmountVol() {
+ int i, rc;
+
+ if (getState() != Volume::State_Mounted) {
+ LOGE("Volume %s unmount request when not mounted", getLabel());
+ errno = EINVAL;
+ return -1;
+ }
+
+ setState(Volume::State_Unmounting);
+ usleep(1000 * 200); // Give the framework some time to react
+ for (i = 0; i < 10; i++) {
+ rc = umount(getMountpoint());
+ if (!rc)
+ break;
+
+ if (rc && (errno == EINVAL || errno == ENOENT)) {
+ rc = 0;
+ break;
+ }
+
+ LOGW("Volume %s unmount attempt %d failed (%s)",
+ getLabel(), i + 1, strerror(errno));
+
+ if (i < 5) {
+ usleep(1000 * 250);
+ } else {
+ KillProcessesWithOpenFiles(getMountpoint(),
+ (i < 7 ? 0 : 1),
+ NULL, 0);
+ usleep(1000 * 250);
+ }
+ }
+
+ if (!rc) {
+ LOGI("Volume %s unmounted sucessfully", getLabel());
+ setState(Volume::State_Idle);
+ mCurrentlyMountedKdev = -1;
+ return 0;
+ }
+
+ LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno));
+ setState(Volume::State_Mounted);
+ return -1;
+}
+
+int Volume::initializeMbr(const char *deviceNode) {
+ int fd, rc;
+ unsigned char block[512];
+ struct dos_partition part;
+ unsigned int nr_sec;
+
+ if ((fd = open(deviceNode, O_RDWR)) < 0) {
+ LOGE("Error opening disk file (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
+ LOGE("Unable to get device size (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ memset(&part, 0, sizeof(part));
+ part.dp_flag = 0x80;
+ part.dp_typ = 0xc;
+ part.dp_start = ((1024 * 64) / 512) + 1;
+ part.dp_size = nr_sec - part.dp_start;
+
+ memset(block, 0, sizeof(block));
+ block[0x1fe] = 0x55;
+ block[0x1ff] = 0xaa;
+
+ dos_partition_enc(block + DOSPARTOFF, &part);
+
+ if (write(fd, block, sizeof(block)) < 0) {
+ LOGE("Error writing MBR (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (ioctl(fd, BLKRRPART, NULL) < 0) {
+ LOGE("Error re-reading partition table (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
return 0;
}
diff --git a/Volume.h b/Volume.h
index 689cd53..7306e4e 100644
--- a/Volume.h
+++ b/Volume.h
@@ -20,6 +20,7 @@
#include <utils/List.h>
class NetlinkEvent;
+class VolumeManager;
class Volume {
private:
@@ -27,38 +28,53 @@
public:
static const int State_Init = -1;
+ static const int State_NoMedia = 0;
static const int State_Idle = 1;
static const int State_Pending = 2;
- static const int State_Mounted = 3;
- static const int State_Checking = 4;
- static const int State_Formatting = 5;
+ static const int State_Checking = 3;
+ static const int State_Mounted = 4;
+ static const int State_Unmounting = 5;
+ static const int State_Formatting = 6;
+ static const int State_Shared = 7;
+ static const int State_SharedMnt = 8;
protected:
char *mLabel;
char *mMountpoint;
+ VolumeManager *mVm;
+
+ /*
+ * The major/minor tuple of the currently mounted filesystem.
+ */
+ dev_t mCurrentlyMountedKdev;
public:
- Volume(const char *label, const char *mount_point);
+ Volume(VolumeManager *vm, const char *label, const char *mount_point);
virtual ~Volume();
- int mount();
- int unmount();
+ int mountVol();
+ int unmountVol();
+ int formatVol();
const char *getLabel() { return mLabel; }
const char *getMountpoint() { return mMountpoint; }
int getState() { return mState; }
virtual int handleBlockEvent(NetlinkEvent *evt);
+ virtual dev_t getDiskDevice();
+ virtual void handleVolumeShared();
+ virtual void handleVolumeUnshared();
protected:
void setState(int state);
- virtual int prepareToMount(int *major, int *minor) = 0;
+ virtual int getDeviceNodes(dev_t *devs, int max) = 0;
int createDeviceNode(const char *path, int major, int minor);
private:
- int checkFilesystem(const char *nodepath);
+ int initializeMbr(const char *deviceNode);
+ bool isMountpointMounted(const char *path);
};
typedef android::List<Volume *> VolumeCollection;
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 024645d..ba92768 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -18,6 +18,12 @@
#include <stdlib.h>
#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"
@@ -27,7 +33,12 @@
#include "VolumeManager.h"
#include "DirectVolume.h"
-#include "ErrorCode.h"
+#include "ResponseCode.h"
+#include "Loop.h"
+#include "Fat.h"
+#include "Devmapper.h"
+
+extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int);
VolumeManager *VolumeManager::sInstance = NULL;
@@ -40,11 +51,15 @@
VolumeManager::VolumeManager() {
mBlockDevices = new BlockDeviceCollection();
mVolumes = new VolumeCollection();
+ mActiveContainers = new AsecIdCollection();
mBroadcaster = NULL;
+ mUsbMassStorageConnected = false;
}
VolumeManager::~VolumeManager() {
delete mBlockDevices;
+ delete mVolumes;
+ delete mActiveContainers;
}
int VolumeManager::start() {
@@ -60,6 +75,43 @@
return 0;
}
+void VolumeManager::notifyUmsConnected(bool connected) {
+ char msg[255];
+
+ if (connected) {
+ mUsbMassStorageConnected = true;
+ } else {
+ mUsbMassStorageConnected = false;
+ }
+ snprintf(msg, sizeof(msg), "Share method ums now %s",
+ (connected ? "available" : "unavailable"));
+
+ getBroadcaster()->sendBroadcast(ResponseCode::ShareAvailabilityChange,
+ msg, false);
+}
+
+void VolumeManager::handleSwitchEvent(NetlinkEvent *evt) {
+ const char *devpath = evt->findParam("DEVPATH");
+ const char *name = evt->findParam("SWITCH_NAME");
+ const char *state = evt->findParam("SWITCH_STATE");
+
+ if (!name || !state) {
+ LOGW("Switch %s event missing name/state info", devpath);
+ return;
+ }
+
+ if (!strcmp(name, "usb_mass_storage")) {
+
+ if (!strcmp(state, "online")) {
+ notifyUmsConnected(true);
+ } else {
+ notifyUmsConnected(false);
+ }
+ } else {
+ LOGW("Ignoring unknown switch '%s'", name);
+ }
+}
+
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
const char *devpath = evt->findParam("DEVPATH");
@@ -68,13 +120,18 @@
bool hit = false;
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
if (!(*it)->handleBlockEvent(evt)) {
+#ifdef NETLINK_DEBUG
+ LOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
+#endif
hit = true;
break;
}
}
if (!hit) {
+#ifdef NETLINK_DEBUG
LOGW("No volumes handled block event for '%s'", devpath);
+#endif
}
}
@@ -86,10 +143,358 @@
asprintf(&buffer, "%s %s %d",
(*i)->getLabel(), (*i)->getMountpoint(),
(*i)->getState());
- cli->sendMsg(ErrorCode::VolumeListResult, buffer, false);
+ cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
free(buffer);
}
- cli->sendMsg(ErrorCode::CommandOkay, "Volumes listed.", false);
+ cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
+ return 0;
+}
+
+int VolumeManager::formatVolume(const char *label) {
+ Volume *v = lookupVolume(label);
+
+ if (!v) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ return v->formatVol();
+}
+
+int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
+ char mountPoint[255];
+
+ snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+ snprintf(buffer, maxlen, "/asec/%s", id);
+ return 0;
+}
+
+int VolumeManager::createAsec(const char *id, unsigned int numSectors,
+ 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, numSectors)) {
+ LOGE("ASEC image file creation failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ char loopDevice[255];
+ if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) {
+ LOGE("ASEC loop device creation failed (%s)", strerror(errno));
+ unlink(asecFileName);
+ return -1;
+ }
+
+ char dmDevice[255];
+ bool cleanupDm = false;
+
+ if (strcmp(key, "none")) {
+ if (Devmapper::create(id, loopDevice, key, numSectors, dmDevice,
+ sizeof(dmDevice))) {
+ LOGE("ASEC device mapping failed (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ unlink(asecFileName);
+ return -1;
+ }
+ cleanupDm = true;
+ } else {
+ strcpy(dmDevice, loopDevice);
+ }
+
+ if (Fat::format(dmDevice)) {
+ LOGE("ASEC FAT format failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
+ unlink(asecFileName);
+ return -1;
+ }
+
+ char mountPoint[255];
+
+ snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+ if (mkdir(mountPoint, 0777)) {
+ if (errno != EEXIST) {
+ LOGE("Mountpoint creation failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
+ unlink(asecFileName);
+ return -1;
+ }
+ }
+
+ if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid,
+ 0, 0000, false)) {
+// 0, 0007, false)) {
+ LOGE("ASEC FAT mount failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
+ unlink(asecFileName);
+ return -1;
+ }
+
+ mActiveContainers->push_back(strdup(id));
+ 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);
+ // XXX:
+ if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) {
+ LOGE("ASEC finalize mount failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ LOGD("ASEC %s finalized", id);
+ return 0;
+}
+
+int VolumeManager::renameAsec(const char *id1, const char *id2) {
+ char *asecFilename1;
+ char *asecFilename2;
+ char mountPoint[255];
+
+ asprintf(&asecFilename1, "/sdcard/android_secure/%s.asec", id1);
+ asprintf(&asecFilename2, "/sdcard/android_secure/%s.asec", id2);
+
+ snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id1);
+ if (isMountpointMounted(mountPoint)) {
+ LOGW("Rename attempt when src mounted");
+ errno = EBUSY;
+ goto out_err;
+ }
+
+ if (!access(asecFilename2, F_OK)) {
+ LOGE("Rename attempt when dst exists");
+ errno = EADDRINUSE;
+ goto out_err;
+ }
+
+ if (rename(asecFilename1, asecFilename2)) {
+ LOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
+ goto out_err;
+ }
+
+ free(asecFilename1);
+ free(asecFilename2);
+ return 0;
+
+out_err:
+ free(asecFilename1);
+ free(asecFilename2);
+ return -1;
+}
+
+int VolumeManager::unmountAsec(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)) {
+ LOGE("Unmount request for ASEC %s when not mounted", id);
+ errno = EINVAL;
+ return -1;
+ }
+
+ 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));
+
+ if (i >= 5) {
+ KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1),
+ NULL, 0);
+ }
+ usleep(1000 * 250);
+ }
+
+ if (rc) {
+ LOGE("Failed to unmount ASEC %s", id);
+ return -1;
+ }
+
+ if (rmdir(mountPoint)) {
+ LOGE("Failed to rmdir mountpoint (%s)", strerror(errno));
+ }
+
+ if (Devmapper::destroy(id) && errno != ENXIO) {
+ LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
+ }
+
+ char loopDevice[255];
+ if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
+ Loop::destroyByDevice(loopDevice);
+ }
+
+ AsecIdCollection::iterator it;
+ for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
+ if (!strcmp(*it, id)) {
+ free(*it);
+ mActiveContainers->erase(it);
+ break;
+ }
+ }
+ if (it == mActiveContainers->end()) {
+ LOGW("mActiveContainers is inconsistent!");
+ }
+ 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)) {
+ LOGD("Unmounting container before destroy");
+ if (unmountAsec(id)) {
+ LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
+ return -1;
+ }
+ }
+
+ if (unlink(asecFileName)) {
+ LOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
+ return -1;
+ }
+
+ 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::create(asecFileName, loopDevice, sizeof(loopDevice))) {
+ LOGE("ASEC loop device creation failed (%s)", strerror(errno));
+ return -1;
+ }
+ LOGD("New loop device created at %s", loopDevice);
+ } else {
+ LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
+ }
+
+ char dmDevice[255];
+ bool cleanupDm = false;
+ if (strcmp(key, "none")) {
+ if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) {
+ unsigned int nr_sec = 0;
+ int fd;
+
+ if ((fd = open(loopDevice, O_RDWR)) < 0) {
+ LOGE("Failed to open loopdevice (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
+
+ if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
+ LOGE("Failed to get loop size (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ if (Devmapper::create(id, loopDevice, key, nr_sec,
+ dmDevice, sizeof(dmDevice))) {
+ LOGE("ASEC device mapping failed (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
+ LOGD("New devmapper instance created at %s", dmDevice);
+ } else {
+ LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
+ }
+ cleanupDm = true;
+ } else {
+ strcpy(dmDevice, loopDevice);
+ }
+
+ if (mkdir(mountPoint, 0777)) {
+ if (errno != EEXIST) {
+ LOGE("Mountpoint creation failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
+ }
+
+ if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0,
+ 0222, false)) {
+// 0227, false)) {
+ LOGE("ASEC mount failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
+
+ mActiveContainers->push_back(strdup(id));
+ LOGD("ASEC %s mounted", id);
return 0;
}
@@ -101,12 +506,162 @@
return -1;
}
+ return v->mountVol();
+}
+
+int VolumeManager::shareAvailable(const char *method, bool *avail) {
+
+ if (strcmp(method, "ums")) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (mUsbMassStorageConnected)
+ *avail = true;
+ else
+ *avail = false;
+ return 0;
+}
+
+int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
+ Volume *v = lookupVolume(label);
+
+ if (!v) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (strcmp(method, "ums")) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (v->getState() != Volume::State_Shared) {
+ *enabled = false;
+ } else {
+ *enabled = true;
+ }
+ return 0;
+}
+
+int VolumeManager::simulate(const char *cmd, const char *arg) {
+
+ if (!strcmp(cmd, "ums")) {
+ if (!strcmp(arg, "connect")) {
+ notifyUmsConnected(true);
+ } else if (!strcmp(arg, "disconnect")) {
+ notifyUmsConnected(false);
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int VolumeManager::shareVolume(const char *label, const char *method) {
+ Volume *v = lookupVolume(label);
+
+ if (!v) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /*
+ * Eventually, we'll want to support additional share back-ends,
+ * some of which may work while the media is mounted. For now,
+ * we just support UMS
+ */
+ if (strcmp(method, "ums")) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (v->getState() == Volume::State_NoMedia) {
+ errno = ENODEV;
+ return -1;
+ }
+
if (v->getState() != Volume::State_Idle) {
+ // You need to unmount manually befoe sharing
errno = EBUSY;
return -1;
}
- return v->mount();
+ dev_t d = v->getDiskDevice();
+ if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
+ // This volume does not support raw disk access
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd;
+ char nodepath[255];
+ snprintf(nodepath,
+ sizeof(nodepath), "/dev/block/vold/%d:%d",
+ MAJOR(d), MINOR(d));
+
+ if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
+ O_WRONLY)) < 0) {
+ LOGE("Unable to open ums lunfile (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (write(fd, nodepath, strlen(nodepath)) < 0) {
+ LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ v->handleVolumeShared();
+ return 0;
+}
+
+int VolumeManager::unshareVolume(const char *label, const char *method) {
+ Volume *v = lookupVolume(label);
+
+ if (!v) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (strcmp(method, "ums")) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (v->getState() != Volume::State_Shared) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ dev_t d = v->getDiskDevice();
+
+ int fd;
+ char nodepath[255];
+ snprintf(nodepath,
+ sizeof(nodepath), "/dev/block/vold/%d:%d",
+ MAJOR(d), MINOR(d));
+
+ if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
+ LOGE("Unable to open ums lunfile (%s)", strerror(errno));
+ return -1;
+ }
+
+ char ch = 0;
+ if (write(fd, &ch, 1) < 0) {
+ LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ v->handleVolumeUnshared();
+ return 0;
}
int VolumeManager::unmountVolume(const char *label) {
@@ -117,20 +672,72 @@
return -1;
}
+ if (v->getState() == Volume::State_NoMedia) {
+ errno = ENODEV;
+ return -1;
+ }
+
if (v->getState() != Volume::State_Mounted) {
+ LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
+ v->getState());
errno = EBUSY;
return -1;
}
- return v->unmount();
+ while(mActiveContainers->size()) {
+ AsecIdCollection::iterator it = mActiveContainers->begin();
+ LOGI("Unmounting ASEC %s (dependant on %s)", *it, v->getMountpoint());
+ if (unmountAsec(*it)) {
+ LOGE("Failed to unmount ASEC %s (%s) - unmount of %s may fail!", *it,
+ strerror(errno), v->getMountpoint());
+ }
+ }
+
+ return v->unmountVol();
}
+/*
+ * Looks up a volume by it's label or mount-point
+ */
Volume *VolumeManager::lookupVolume(const char *label) {
VolumeCollection::iterator i;
for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
- if (!strcmp(label, (*i)->getLabel()))
- return (*i);
+ if (label[0] == '/') {
+ if (!strcmp(label, (*i)->getMountpoint()))
+ return (*i);
+ } else {
+ if (!strcmp(label, (*i)->getLabel()))
+ return (*i);
+ }
}
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;
+}
+
diff --git a/VolumeManager.h b/VolumeManager.h
index c3d65e0..f989cdb 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -25,6 +25,8 @@
#include "BlockDevice.h"
#include "Volume.h"
+typedef android::List<char *> AsecIdCollection;
+
class VolumeManager {
private:
static VolumeManager *sInstance;
@@ -34,6 +36,8 @@
BlockDeviceCollection *mBlockDevices;
VolumeCollection *mVolumes;
+ AsecIdCollection *mActiveContainers;
+ bool mUsbMassStorageConnected;
public:
virtual ~VolumeManager();
@@ -42,12 +46,30 @@
int stop();
void handleBlockEvent(NetlinkEvent *evt);
+ void handleSwitchEvent(NetlinkEvent *evt);
int addVolume(Volume *v);
int listVolumes(SocketClient *cli);
int mountVolume(const char *label);
int unmountVolume(const char *label);
+ int shareVolume(const char *label, const char *method);
+ int unshareVolume(const char *label, const char *method);
+ int shareAvailable(const char *method, bool *avail);
+ int shareEnabled(const char *path, const char *method, bool *enabled);
+ int simulate(const char *cmd, const char *arg);
+ int formatVolume(const char *label);
+ int createAsec(const char *id, unsigned numSectors, 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 unmountAsec(const char *id);
+ int renameAsec(const char *id1, const char *id2);
+ int getAsecMountPath(const char *id, char *buffer, int maxlen);
+
+ // XXX: This should be moved private once switch uevents are working
+ void notifyUmsConnected(bool connected);
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
SocketListener *getBroadcaster() { return mBroadcaster; }
@@ -57,5 +79,6 @@
private:
VolumeManager();
Volume *lookupVolume(const char *label);
+ bool isMountpointMounted(const char *mp);
};
#endif
diff --git a/diskmbr.h b/diskmbr.h
new file mode 100644
index 0000000..f160575
--- /dev/null
+++ b/diskmbr.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)disklabel.h 8.2 (Berkeley) 7/10/94
+ * $FreeBSD: src/sys/sys/diskmbr.h,v 1.99.2.1 2005/01/31 23:26:56 imp Exp $
+ */
+
+#ifndef _SYS_DISKMBR_H_
+#define _SYS_DISKMBR_H_
+
+#define DOSBBSECTOR 0 /* DOS boot block relative sector number */
+#define DOSPARTOFF 446
+#define DOSPARTSIZE 16
+#define NDOSPART 4
+#define NEXTDOSPART 32
+#define DOSMAGICOFFSET 510
+#define DOSMAGIC 0xAA55
+
+#define DOSPTYP_386BSD 0xa5 /* 386BSD partition type */
+#define DOSPTYP_LINSWP 0x82 /* Linux swap partition */
+#define DOSPTYP_LINUX 0x83 /* Linux partition */
+#define DOSPTYP_PMBR 0xee /* GPT Protective MBR */
+#define DOSPTYP_EXT 5 /* DOS extended partition */
+#define DOSPTYP_EXTLBA 15 /* DOS extended partition */
+
+struct dos_partition {
+ unsigned char dp_flag; /* bootstrap flags */
+ unsigned char dp_shd; /* starting head */
+ unsigned char dp_ssect; /* starting sector */
+ unsigned char dp_scyl; /* starting cylinder */
+ unsigned char dp_typ; /* partition type */
+ unsigned char dp_ehd; /* end head */
+ unsigned char dp_esect; /* end sector */
+ unsigned char dp_ecyl; /* end cylinder */
+ u_int32_t dp_start; /* absolute starting sector number */
+ u_int32_t dp_size; /* partition size in sectors */
+};
+#ifdef CTASSERT
+CTASSERT(sizeof (struct dos_partition) == DOSPARTSIZE);
+#endif
+
+#define DPSECT(s) ((s) & 0x3f) /* isolate relevant bits of sector */
+#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */
+
+#define DIOCSMBR _IOW('M', 129, u_char[512])
+
+#endif /* !_SYS_DISKMBR_H_ */
diff --git a/geom_mbr_enc.c b/geom_mbr_enc.c
new file mode 100644
index 0000000..f1f8339
--- /dev/null
+++ b/geom_mbr_enc.c
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Functions to encode or decode struct dos_partition into a bytestream
+ * of correct endianess and packing. These functions do no validation
+ * or sanity checking, they only pack/unpack the fields correctly.
+ *
+ * NB! This file must be usable both in kernel and userland.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+
+#include "diskmbr.h"
+
+static __inline uint32_t __unused
+le32dec(const void *buf)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+
+ return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+static __inline void
+le32enc(void *pp, uint32_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = u & 0xff;
+ p[1] = (u >> 8) & 0xff;
+ p[2] = (u >> 16) & 0xff;
+ p[3] = (u >> 24) & 0xff;
+}
+
+void
+dos_partition_dec(void const *pp, struct dos_partition *d)
+{
+ unsigned char const *p = pp;
+
+ d->dp_flag = p[0];
+ d->dp_shd = p[1];
+ d->dp_ssect = p[2];
+ d->dp_scyl = p[3];
+ d->dp_typ = p[4];
+ d->dp_ehd = p[5];
+ d->dp_esect = p[6];
+ d->dp_ecyl = p[7];
+ d->dp_start = le32dec(p + 8);
+ d->dp_size = le32dec(p + 12);
+}
+
+void
+dos_partition_enc(void *pp, struct dos_partition *d)
+{
+ unsigned char *p = pp;
+
+ p[0] = d->dp_flag;
+ p[1] = d->dp_shd;
+ p[2] = d->dp_ssect;
+ p[3] = d->dp_scyl;
+ p[4] = d->dp_typ;
+ p[5] = d->dp_ehd;
+ p[6] = d->dp_esect;
+ p[7] = d->dp_ecyl;
+ le32enc(p + 8, d->dp_start);
+ le32enc(p + 12, d->dp_size);
+}
diff --git a/main.cpp b/main.cpp
index 1485a20..2412612 100644
--- a/main.cpp
+++ b/main.cpp
@@ -42,7 +42,9 @@
CommandListener *cl;
NetlinkManager *nm;
- LOGI("Vold 2.0 firing up");
+ LOGI("Vold 2.0 (the revenge) firing up");
+
+ mkdir("/dev/block/vold", 0755);
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
@@ -55,6 +57,7 @@
exit(1);
};
+
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
@@ -75,6 +78,35 @@
}
coldboot("/sys/block");
+ /*
+ * Switch uevents are broken.
+ * For now we manually bootstrap
+ * the ums switch
+ */
+ {
+ FILE *fp;
+ char state[255];
+
+ if ((fp = fopen("/sys/devices/virtual/switch/usb_mass_storage/state",
+ "r"))) {
+ if (!fgets(state, sizeof(state), fp)) {
+ LOGE("Failed to read switch state (%s)", strerror(errno));
+ fclose(fp);
+ exit(1);
+ }
+ if (!strncmp(state, "online", 6)) {
+ LOGD("Bootstrapped ums is connected");
+ vm->notifyUmsConnected(true);
+ } else {
+ LOGD("Bootstrapped ums is disconnected");
+ vm->notifyUmsConnected(false);
+ }
+ fclose(fp);
+ } else {
+ LOGW("No UMS switch available");
+ }
+ }
+// coldboot("/sys/class/switch");
/*
* Now that we're up, we can respond to commands
@@ -183,7 +215,11 @@
goto out_syntax;
}
- dv = new DirectVolume(label, mount_point, atoi(part));
+ if (!strcmp(part, "auto")) {
+ dv = new DirectVolume(vm, label, mount_point, -1);
+ } else {
+ dv = new DirectVolume(vm, label, mount_point, atoi(part));
+ }
while((sysfs_path = strsep(&next, " \t"))) {
if (dv->addPath(sysfs_path)) {