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)) {