vold2: Get mounting/unmounting/formatting/sharing working
Signed-off-by: San Mehat <san@google.com>
diff --git a/Android.mk b/Android.mk
index dda81ed..756bbe9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,7 +15,9 @@
BlockDevice.cpp \
Volume.cpp \
DirectVolume.cpp \
- logwrapper.c
+ logwrapper.c \
+ ProcessKiller.c \
+ geom_mbr_enc.c
LOCAL_MODULE:= vold
diff --git a/CommandListener.cpp b/CommandListener.cpp
index a7bc807..702ae32 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -27,15 +27,18 @@
#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 MountCmd());
+ registerCmd(new UnmountCmd());
+ registerCmd(new ShareCmd());
+ registerCmd(new UnshareCmd());
+ registerCmd(new ShareAvailableCmd());
+ registerCmd(new SimulateCmd());
+ registerCmd(new FormatCmd());
}
CommandListener::ListVolumesCmd::ListVolumesCmd() :
@@ -47,58 +50,113 @@
return VolumeManager::Instance()->listVolumes(cli);
}
-CommandListener::MountVolumeCmd::MountVolumeCmd() :
- VoldCommand("mount_volume") {
+CommandListener::MountCmd::MountCmd() :
+ VoldCommand("mount") {
}
-int CommandListener::MountVolumeCmd::runCommand(SocketClient *cli,
+int CommandListener::MountCmd::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);
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to mount volume.", true);
} else {
- cli->sendMsg(ErrorCode::CommandOkay, "Volume mounted.", false);
+ cli->sendMsg(ResponseCode::CommandOkay, "Volume mounted.", false);
}
return 0;
}
-CommandListener::UnmountVolumeCmd::UnmountVolumeCmd() :
- VoldCommand("unmount_volume") {
+CommandListener::UnmountCmd::UnmountCmd() :
+ VoldCommand("unmount") {
}
-int CommandListener::UnmountVolumeCmd::runCommand(SocketClient *cli,
+int CommandListener::UnmountCmd::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 (VolumeManager::Instance()->unmountVolume(argv[1])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to unmount volume.", true);
} else {
- cli->sendMsg(ErrorCode::CommandOkay, "Volume unmounted.", false);
+ cli->sendMsg(ResponseCode::CommandOkay, "Volume unmounted.", false);
}
return 0;
}
-CommandListener::ShareVolumeCmd::ShareVolumeCmd() :
- VoldCommand("share_volume") {
+CommandListener::ShareCmd::ShareCmd() :
+ VoldCommand("share") {
}
-int CommandListener::ShareVolumeCmd::runCommand(SocketClient *cli,
+int CommandListener::ShareCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
- VolumeManager *nm = VolumeManager::Instance();
- errno = ENOSYS;
- cli->sendMsg(ErrorCode::OperationFailed, "Failed to share volume", true);
+ if (VolumeManager::Instance()->shareVolume(argv[1], argv[2])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to share volume.", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Volume shared.", false);
+ }
+
return 0;
}
-CommandListener::UnshareVolumeCmd::UnshareVolumeCmd() :
- VoldCommand("unshare_volume") {
+CommandListener::UnshareCmd::UnshareCmd() :
+ VoldCommand("unshare") {
}
-int CommandListener::UnshareVolumeCmd::runCommand(SocketClient *cli,
+int CommandListener::UnshareCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
- VolumeManager *nm = VolumeManager::Instance();
- errno = ENOSYS;
- cli->sendMsg(ErrorCode::OperationFailed, "Failed to unshare volume", true);
+ if (VolumeManager::Instance()->unshareVolume(argv[1], argv[2])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to unshare volume.", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Volume unshared.", false);
+ }
+
+ return 0;
+}
+
+CommandListener::ShareAvailableCmd::ShareAvailableCmd() :
+ VoldCommand("share_available") {
+}
+
+int CommandListener::ShareAvailableCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ bool avail = false;
+
+ if (VolumeManager::Instance()->shareAvailable(argv[1], &avail)) {
+ cli->sendMsg(ResponseCode::OperationFailed,
+ "Failed to determine share availability", true);
+ } else {
+ cli->sendMsg(ResponseCode::ShareAvailabilityResult,
+ (avail ? "Share available" : "Share unavailable"),
+ false);
+ }
+ return 0;
+}
+
+CommandListener::SimulateCmd::SimulateCmd() :
+ VoldCommand("simulate") {
+}
+
+int CommandListener::SimulateCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (VolumeManager::Instance()->simulate(argv[1], argv[2])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to execute.", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Simulation executed.", false);
+ }
+
+ return 0;
+}
+
+CommandListener::FormatCmd::FormatCmd() :
+ VoldCommand("format") {
+}
+
+int CommandListener::FormatCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (VolumeManager::Instance()->formatVolume(argv[1])) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to format", true);
+ } else {
+ cli->sendMsg(ResponseCode::CommandOkay, "Volume formatted.", false);
+ }
+
return 0;
}
diff --git a/CommandListener.h b/CommandListener.h
index 9c770a2..5acec5b 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -34,31 +34,52 @@
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class MountVolumeCmd : public VoldCommand {
+ class MountCmd : public VoldCommand {
public:
- MountVolumeCmd();
- virtual ~MountVolumeCmd() {}
+ MountCmd();
+ virtual ~MountCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class UnmountVolumeCmd : public VoldCommand {
+ class UnmountCmd : public VoldCommand {
public:
- UnmountVolumeCmd();
- virtual ~UnmountVolumeCmd() {}
+ UnmountCmd();
+ virtual ~UnmountCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class ShareVolumeCmd : public VoldCommand {
+ class ShareCmd : public VoldCommand {
public:
- ShareVolumeCmd();
- virtual ~ShareVolumeCmd() {}
+ ShareCmd();
+ virtual ~ShareCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class UnshareVolumeCmd : public VoldCommand {
+ class UnshareCmd : public VoldCommand {
public:
- UnshareVolumeCmd();
- virtual ~UnshareVolumeCmd() {}
+ UnshareCmd();
+ virtual ~UnshareCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
+ class ShareAvailableCmd : public VoldCommand {
+ public:
+ ShareAvailableCmd();
+ virtual ~ShareAvailableCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
+ class SimulateCmd : public VoldCommand {
+ public:
+ SimulateCmd();
+ virtual ~SimulateCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
+ class FormatCmd : public VoldCommand {
+ public:
+ FormatCmd();
+ virtual ~FormatCmd() {}
int runCommand(SocketClient *c, int argc, char ** argv);
};
diff --git a/DirectVolume.cpp b/DirectVolume.cpp
index cd12cf4..1fda955 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,9 @@
void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
mDiskMajor = atoi(evt->findParam("MAJOR"));
- mDiskMinor = atoi(evt->findParam("MAJOR"));
+ mDiskMinor = atoi(evt->findParam("MINOR"));
mDiskNumParts = atoi(evt->findParam("NPARTS"));
+ char msg[255];
int partmask = 0;
int i;
@@ -94,13 +137,22 @@
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) {
@@ -112,54 +164,122 @@
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());
+ mDiskNumParts = atoi(evt->findParam("NPARTS"));
+ 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"));
+ int part_num = atoi(evt->findParam("PARTN"));
+ 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/NetlinkHandler.cpp b/NetlinkHandler.cpp
index 19ec932..5f897a2 100644
--- a/NetlinkHandler.cpp
+++ b/NetlinkHandler.cpp
@@ -52,6 +52,8 @@
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 {
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/ErrorCode.h b/ResponseCode.h
similarity index 67%
rename from ErrorCode.h
rename to ResponseCode.h
index 328b2b1..4f7237c 100644
--- a/ErrorCode.h
+++ b/ResponseCode.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef _ERRORCODE_H
-#define _ERRORCODE_H
+#ifndef _RESPONSECODE_H
+#define _RESPONSECODE_H
-class ErrorCode {
+class ResponseCode {
public:
// 100 series - Requestion action was initiated; expect another reply
// before proceeding with a new command.
@@ -27,6 +27,7 @@
// 200 series - Requested action has been successfully completed
static const int CommandOkay = 200;
+ static const int ShareAvailabilityResult = 210;
// 400 series - The command was accepted but the requested action
// did not take place.
@@ -38,6 +39,16 @@
static const int CommandParameterError = 501;
// 600 series - Unsolicited broadcasts
- static const int UnsolicitedInformational = 600;
+ 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..b9f030b 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -24,21 +24,62 @@
#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"
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 *);
+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 char MKDOSFS_PATH[] = "/system/bin/newfs_msdos";
-Volume::Volume(const char *label, const char *mount_point) {
+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(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 +87,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,38 +135,227 @@
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("Volume %s (%s) MBR being initialized", 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);
+ LOGI("Volume %s (%s) being formatted", getLabel(), devicePath);
+
+ if (doFormatVfat(devicePath)) {
+ LOGE("Failed to format (%s)", strerror(errno));
+ goto err;
+ }
+
+ LOGI("Volume %s (%s) formatted sucessfully", getLabel(), devicePath);
return 0;
+err:
+ return -1;
+}
+
+bool Volume::isMountpointMounted(const char *path) {
+ 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, 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;
+ }
+
+ n = getDeviceNodes((dev_t *) &deviceNodes, 4);
+ if (!n) {
+ LOGE("Failed to get device nodes (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < n; i++) {
+ char devicePath[255];
+
+ sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
+ MINOR(deviceNodes[i]));
+
+ LOGI("%s being considered for volume %s\n", devicePath, getLabel());
+
+ errno = 0;
+ if ((rc = checkFilesystem(devicePath))) {
+ if (errno == ENODATA) {
+ LOGW("%s does not contain a FAT filesystem\n", devicePath);
+ continue;
+ } else {
+ /* Badness - abort the mount */
+ LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
+ snprintf(errmsg, sizeof(errmsg),
+ "Volume %s %s mount failed - filesystem check failed",
+ getLabel(), getMountpoint());
+ mVm->getBroadcaster()->sendBroadcast(
+ ResponseCode::VolumeMountFailedDamaged,
+ errmsg, false);
+ setState(Volume::State_Idle);
+ goto out;
+ }
+ }
+
+ LOGI("%s checks out - attempting to mount\n", devicePath);
+ errno = 0;
+ if (!(rc = doMountVfat(devicePath, getMountpoint()))) {
+ LOGI("%s sucessfully mounted for volume %s\n", devicePath,
+ getLabel());
+ setState(Volume::State_Mounted);
+ mCurrentlyMountedKdev = deviceNodes[i];
+ goto out;
+ }
+
+ LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
+ }
+
+ // XXX: Doesn't handle multiple partitions properly
+ if (errno == ENODATA) {
+ snprintf(errmsg, sizeof(errmsg),
+ "Volume %s %s mount failed - no supported file-systems",
+ getLabel(), getMountpoint());
+ mVm->getBroadcaster()->sendBroadcast(
+ ResponseCode::VolumeMountFailedBlank,
+ errmsg, false);
+ }
+
+
+ LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
+ setState(Volume::State_Idle);
+
+out:
+ return rc;
+}
+
+
+int Volume::doMountVfat(const char *deviceNode, const char *mountPoint)
+{
+ int rc;
+ unsigned long flags;
+
+ flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+
+ /*
+ * 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'.");
+ rc = mount(deviceNode, mountPoint, (const char *) "vfat", (unsigned long) flags,
+ (const void *) "utf8,uid=1000,gid=1015,fmask=000,dmask=000,shortname=mixed");
+ } else {
+ /*
+ * The mount masks restrict access so that:
+ * 1. The 'system' user cannot access the SD card at all -
+ * (protects system_server from grabbing file references)
+ * 2. Group users can RWX
+ * 3. Others can only RX
+ */
+ rc = mount(deviceNode, mountPoint, "vfat", flags,
+ "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
+ }
+
+ if (rc && errno == EROFS) {
+ LOGE("%s appears to be a read only filesystem - retrying mount RO",
+ deviceNode);
+ flags |= MS_RDONLY;
+ rc = mount(deviceNode, mountPoint, "vfat", flags,
+ "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
+ }
+
+ if (rc == 0) {
+ 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 Volume::checkFilesystem(const char *nodepath) {
@@ -152,6 +409,133 @@
return 0;
}
-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);
+ 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;
+}
+
+int Volume::doFormatVfat(const char *deviceNode) {
+ unsigned int nr_sec;
+ int fd;
+
+ 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;
+ }
+ close(fd);
+
+ const char *args[7];
+ int rc;
+ args[0] = MKDOSFS_PATH;
+ args[1] = "-F";
+ if ((nr_sec * 512) <= ((unsigned int) (1024*1024*1024) * 2))
+ args[2] = "16";
+ else
+ args[2] = "32";
+
+ args[3] = "-O";
+ args[4] = "android";
+ args[5] = deviceNode;
+ args[6] = NULL;
+ rc = logwrap(7, 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/Volume.h b/Volume.h
index 689cd53..838cf1c 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,56 @@
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 doMountVfat(const char *deviceNode, const char *mountPoint);
+ int doFormatVfat(const char *deviceNode);
+ 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..4a24ff7 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -18,6 +18,8 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <fcntl.h>
+#include <linux/kdev_t.h>
#define LOG_TAG "Vold"
@@ -27,7 +29,7 @@
#include "VolumeManager.h"
#include "DirectVolume.h"
-#include "ErrorCode.h"
+#include "ResponseCode.h"
VolumeManager *VolumeManager::sInstance = NULL;
@@ -41,6 +43,7 @@
mBlockDevices = new BlockDeviceCollection();
mVolumes = new VolumeCollection();
mBroadcaster = NULL;
+ mUsbMassStorageConnected = false;
}
VolumeManager::~VolumeManager() {
@@ -60,6 +63,37 @@
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 *name = evt->findParam("SWITCH_NAME");
+ const char *state = evt->findParam("SWITCH_STATE");
+
+ 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 +102,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,13 +125,24 @@
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::mountVolume(const char *label) {
Volume *v = lookupVolume(label);
@@ -101,12 +151,140 @@
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::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", 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", 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 +295,35 @@
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();
+ 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;
}
diff --git a/VolumeManager.h b/VolumeManager.h
index c3d65e0..5f5a249 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -34,6 +34,7 @@
BlockDeviceCollection *mBlockDevices;
VolumeCollection *mVolumes;
+ bool mUsbMassStorageConnected;
public:
virtual ~VolumeManager();
@@ -42,12 +43,18 @@
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 simulate(const char *cmd, const char *arg);
+ int formatVolume(const char *label);
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
SocketListener *getBroadcaster() { return mBroadcaster; }
@@ -57,5 +64,6 @@
private:
VolumeManager();
Volume *lookupVolume(const char *label);
+ void notifyUmsConnected(bool connected);
};
#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..3f25ab8 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);
@@ -183,7 +186,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)) {