auto import from //depot/cupcake/@135843
diff --git a/vold/Android.mk b/vold/Android.mk
new file mode 100644
index 0000000..3dd9f87
--- /dev/null
+++ b/vold/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                \
+                  vold.c       \
+                  cmd_dispatch.c \
+                  uevent.c       \
+                  mmc.c          \
+		  misc.c         \
+                  blkdev.c       \
+                  ums.c          \
+                  geom_mbr_enc.c \
+                  volmgr.c       \
+                  media.c        \
+                  volmgr_vfat.c  \
+                  volmgr_ext3.c  \
+                  logwrapper.c   \
+                  ProcessKiller.c\
+                  switch.c       \
+                  format.c       \
+                  devmapper.c
+
+LOCAL_MODULE:= vold
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/vold/ProcessKiller.c b/vold/ProcessKiller.c
new file mode 100644
index 0000000..fc3eb37
--- /dev/null
+++ b/vold/ProcessKiller.c
@@ -0,0 +1,222 @@
+/*
+ * 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 "vold.h"
+
+#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>
+
+
+static boolean ReadSymLink(const char* path, char* link)
+{
+    struct stat s;
+    int length;
+
+    if (lstat(path, &s) < 0)
+        return false;
+    if ((s.st_mode & S_IFMT) != S_IFLNK)
+        return false;
+   
+    // we have a symlink    
+    length = readlink(path, link, PATH_MAX - 1);
+    if (length <= 0) 
+        return false;
+    link[length] = 0;
+    return true;
+}
+
+static boolean 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 true;
+        // 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 false;
+}
+
+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 boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
+{
+    DIR*    dir;
+    struct dirent* de;
+    boolean fileOpen = false;
+    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 false;
+
+    // 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);
+            LOG_ERROR("process %s (%d) has open file %s", name, pid, link);
+            fileOpen = true;
+        }
+    }
+
+    closedir(dir);
+    return fileOpen;
+}
+
+static boolean CheckFileMaps(int pid, const char* mountPoint)
+{
+    FILE*   file;
+    char    buffer[PATH_MAX + 100];
+    boolean mapOpen = false;
+
+    sprintf(buffer, "/proc/%d/maps", pid);
+    file = fopen(buffer, "r");
+    if (!file)
+        return false;
+    
+    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);
+            LOG_ERROR("process %s (%d) has open file map for %s", name, pid, path);
+            mapOpen = true;
+        }
+    }
+    
+    fclose(file);
+    return mapOpen;
+}
+
+static boolean 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);
+        LOG_ERROR("process %s (%d) has %s in %s", name, pid, message, mountPoint);
+        return true;
+    }
+    else
+        return false;
+}
+
+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, boolean sigkill, int *excluded, int num_excluded)
+{
+    DIR*    dir;
+    struct dirent* de;
+
+    LOG_ERROR("KillProcessesWithOpenFiles %s", mountPoint);
+    dir = opendir("/proc");
+    if (!dir) return;
+
+    while ((de = readdir(dir)) != 0)
+    {
+        boolean killed = false;
+        // 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;
+            boolean hit = false;
+
+            for (i = 0; i < num_excluded; i++) {
+                if (pid == excluded[i]) {
+                    LOG_ERROR("I just need a little more TIME captain!");
+                    hit = true;
+                    break;
+                }
+            }
+
+            if (!hit) {
+                LOG_ERROR("Killing process %d", pid);
+                kill(pid, (sigkill ? SIGKILL : SIGTERM));
+            }
+        }
+    }
+
+    closedir(dir);
+}        
diff --git a/vold/blkdev.c b/vold/blkdev.c
new file mode 100644
index 0000000..533bc35
--- /dev/null
+++ b/vold/blkdev.c
@@ -0,0 +1,319 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "diskmbr.h"
+
+#define DEBUG_BLKDEV 0
+
+static blkdev_list_t *list_root = NULL;
+
+static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
+                                int minor, char *type, struct media *media);
+
+static int fat_valid_media(unsigned char media)
+{                       
+        return 0xf8 <= media || media == 0xf0;
+}                               
+
+char *blkdev_get_devpath(blkdev_t *blk)
+{
+    char *dp = malloc(256);
+    sprintf(dp, "%s/vold/%d:%d", DEVPATH, blk->major, blk->minor);
+    return dp;
+}
+
+int blkdev_refresh(blkdev_t *blk)
+{
+    int fd = 0;
+    char *devpath = NULL;
+    unsigned char *block = NULL;
+    int i, rc;
+
+    if (!(block = malloc(512)))
+        goto out;
+
+    /*
+     * Get the device size
+     */
+    devpath = blkdev_get_devpath(blk);
+
+    if ((fd = open(devpath, O_RDONLY)) < 0) {
+        LOGE("Unable to open device '%s' (%s)", devpath, strerror(errno));
+        return -errno;
+    }
+
+    if (ioctl(fd, BLKGETSIZE, &blk->nr_sec)) {
+        LOGE("Unable to get device size (%m)");
+        return -errno;
+    }
+    close(fd);
+    free(devpath);
+
+    /*
+     * Open the disk partition table
+     */
+    devpath = blkdev_get_devpath(blk->disk);
+    if ((fd = open(devpath, O_RDONLY)) < 0) {
+        LOGE("Unable to open device '%s' (%s)", devpath,
+             strerror(errno));
+        free(devpath);
+        return -errno;
+    }
+
+    free(devpath);
+
+    if ((rc = read(fd, block, 512)) != 512) {
+        LOGE("Unable to read device partition table (%d, %s)",
+             rc, strerror(errno));
+        goto out;
+    }
+
+    /*
+     * If we're a disk, then process the partition table. Otherwise we're
+     * a partition so get the partition type
+     */
+
+    if (blk->type == blkdev_disk) {
+        blk->nr_parts = 0;
+
+        if ((block[0x1fe] != 0x55) || (block[0x1ff] != 0xAA)) {
+            LOG_VOL("Disk %d:%d does not contain a partition table",
+                    blk->major, blk->minor);
+            goto out;
+        }
+
+        for (i = 0; i < 4; i++) {
+            struct dos_partition part;
+
+            dos_partition_dec(block + DOSPARTOFF + i * sizeof(struct dos_partition), &part);
+            if (part.dp_flag != 0 && part.dp_flag != 0x80) {
+                struct fat_boot_sector *fb = (struct fat_boot_sector *) &block[0];
+             
+                if (!i && fb->reserved && fb->fats && fat_valid_media(fb->media)) {
+                    LOG_VOL("Detected FAT filesystem in partition table");
+                    break;
+                } else {
+                    LOG_VOL("Partition table looks corrupt");
+                    break;
+                }
+            }
+            if (part.dp_size != 0 && part.dp_typ != 0)
+                blk->nr_parts++;
+        }
+    } else if (blk->type == blkdev_partition) {
+        struct dos_partition part;
+        int part_no = blk->minor -1;
+
+        dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part);
+        blk->part_type = part.dp_typ;
+    }
+
+ out:
+
+    if (block)
+        free(block);
+
+    char tmp[255];
+    char tmp2[32];
+    sprintf(tmp, "%s (blkdev %d:%d), %u secs (%u MB)",
+                 (blk->type == blkdev_disk ? "Disk" : "Partition"),
+                 blk->major, blk->minor,
+                 blk->nr_sec,
+                 (uint32_t) (((uint64_t) blk->nr_sec * 512) / 1024) / 1024);
+
+    if (blk->type == blkdev_disk) 
+        sprintf(tmp2, " %d partitions", blk->nr_parts);
+    else
+        sprintf(tmp2, " type 0x%x", blk->part_type);
+
+    strcat(tmp, tmp2);
+    LOG_VOL(tmp);
+
+    close(fd);
+
+    return 0;
+}
+
+blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type)
+{
+    return _blkdev_create(disk, devpath, major, minor, type, media);
+}
+
+static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
+                                int minor, char *type, struct media *media)
+{
+    blkdev_t *new;
+    struct blkdev_list *list_entry;
+
+    if (disk && disk->type != blkdev_disk) {
+        LOGE("Non disk parent specified for blkdev!");
+        return NULL;
+    }
+
+    if (!(new = malloc(sizeof(blkdev_t))))
+        return NULL;
+
+    memset(new, 0, sizeof(blkdev_t));
+
+    if (!(list_entry = malloc(sizeof(struct blkdev_list)))) {
+        free (new);
+        return NULL;
+    }
+    list_entry->dev = new;
+    list_entry->next = NULL;
+
+    if (!list_root)
+        list_root = list_entry;
+    else {
+        struct blkdev_list *list_scan = list_root;
+        while (list_scan->next)
+            list_scan = list_scan->next;
+        list_scan->next = list_entry;
+    }
+
+    if (devpath)
+        new->devpath = strdup(devpath);
+    new->major = major;
+    new->minor = minor;
+    new->media = media;
+    new->nr_sec = 0xffffffff;
+
+    if (disk)
+        new->disk = disk;
+    else 
+        new->disk = new; // Note the self disk pointer
+
+    /* Create device nodes */
+    char nodepath[255];
+    mode_t mode = 0666 | S_IFBLK;
+    dev_t dev = (major << 8) | minor;
+
+    sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, major, minor);
+    if (mknod(nodepath, mode, dev) < 0) {
+        LOGE("Error making device nodes for '%s' (%s)",
+             nodepath, strerror(errno));
+    }
+
+    if (!strcmp(type, "disk"))
+        new->type = blkdev_disk;
+    else if (!strcmp(type, "partition"))
+        new->type = blkdev_partition;
+    else {
+        LOGE("Unknown block device type '%s'", type);
+        new->type = blkdev_unknown;
+    }
+
+    return new;
+}
+
+void blkdev_destroy(blkdev_t *blkdev)
+{
+    struct blkdev_list *list_next;
+
+    if (list_root->dev == blkdev) {
+        list_next = list_root->next;
+        free (list_root);
+        list_root = list_next;
+    } else {
+        struct blkdev_list *list_scan = list_root;
+        while (list_scan->next->dev != blkdev)
+            list_scan = list_scan -> next;
+        list_next = list_scan->next->next;
+        free(list_scan->next);
+        list_scan->next = list_next;
+    }
+
+    if (blkdev->devpath)
+        free(blkdev->devpath);
+
+    char nodepath[255];
+    sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, blkdev->major, blkdev->minor);
+    unlink(nodepath);
+
+    free(blkdev);
+}
+
+blkdev_t *blkdev_lookup_by_path(char *devpath)
+{
+    struct blkdev_list *list_scan = list_root;
+
+    while (list_scan) {
+        if (!strcmp(list_scan->dev->devpath, devpath)) 
+            return list_scan->dev;
+        list_scan = list_scan->next;
+    }
+    return NULL;
+}
+
+blkdev_t *blkdev_lookup_by_devno(int maj, int min)
+{
+    struct blkdev_list *list_scan = list_root;
+
+    while (list_scan) {
+        if ((list_scan->dev->major == maj) &&
+            (list_scan->dev->minor == min))
+            return list_scan->dev;
+        list_scan = list_scan->next;
+    }
+    return NULL;
+}
+
+/*
+ * Given a disk device, return the number of partitions which 
+ * have yet to be processed.
+ */
+int blkdev_get_num_pending_partitions(blkdev_t *blk)
+{
+    struct blkdev_list *list_scan = list_root;
+    int num = blk->nr_parts;
+
+    if (blk->type != blkdev_disk)
+        return -EINVAL;
+
+    while (list_scan) {
+        if (list_scan->dev->type != blkdev_partition)
+            goto next;
+
+        if (list_scan->dev->major != blk->major)
+            goto next;
+
+        if (list_scan->dev->nr_sec != 0xffffffff &&
+            list_scan->dev->devpath) {
+            num--;
+        }
+ next:
+        list_scan = list_scan->next;
+    }
+    return num;
+}
+
diff --git a/vold/blkdev.h b/vold/blkdev.h
new file mode 100644
index 0000000..e789739
--- /dev/null
+++ b/vold/blkdev.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BLKDEV_H
+#define _BLKDEV_H
+
+#include <sys/types.h>
+
+struct media;
+
+enum blk_type { blkdev_unknown, blkdev_disk, blkdev_partition };
+
+struct blkdev {
+    char          *devpath;
+    enum blk_type type;
+    struct media  *media;
+
+    // If type == blkdev_disk then nr_parts = number of partitions
+    int           nr_parts;
+
+    // If type == blkdev_partition then part_type = partition type
+    uint8_t       part_type;
+    // If type == blkdev_partition
+    struct blkdev *disk;
+
+    unsigned int  nr_sec;
+
+    int           major;
+    int           minor;
+};
+
+struct blkdev_list {
+    struct blkdev      *dev;
+    struct blkdev_list *next;
+};
+
+typedef struct blkdev blkdev_t;
+typedef struct blkdev_list blkdev_list_t;
+
+blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type);
+blkdev_t *blkdev_create_pending_partition(blkdev_t *disk, char *dev_fspath, int major, int minor, struct media *media);
+blkdev_t *blkdev_lookup_by_path(char *devpath);
+blkdev_t *blkdev_lookup_by_devno(int maj, int min);
+char *blkdev_get_devpath(blkdev_t *blk);
+
+void blkdev_destroy(blkdev_t *blk);
+
+int blkdev_get_num_pending_partitions(blkdev_t *blk);
+int blkdev_refresh(blkdev_t *blk);
+#endif
diff --git a/vold/cmd_dispatch.c b/vold/cmd_dispatch.c
new file mode 100644
index 0000000..ab65fd9
--- /dev/null
+++ b/vold/cmd_dispatch.c
@@ -0,0 +1,115 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "vold.h"
+#include "cmd_dispatch.h"
+#include "ums.h"
+#include "volmgr.h"
+
+struct cmd_dispatch {
+    char *cmd;
+    int (* dispatch) (char *);
+};
+
+static void dispatch_cmd(char *cmd);
+static int do_send_ums_status(char *cmd);
+static int do_set_ums_enable(char *cmd);
+static int do_mount_volume(char *cmd);
+static int do_eject_media(char *cmd);
+static int do_format_media(char *cmd);
+
+static struct cmd_dispatch dispatch_table[] = {
+    { VOLD_CMD_ENABLE_UMS,      do_set_ums_enable },
+    { VOLD_CMD_DISABLE_UMS,     do_set_ums_enable },
+    { VOLD_CMD_SEND_UMS_STATUS, do_send_ums_status },
+    { VOLD_CMD_MOUNT_VOLUME,    do_mount_volume },
+    { VOLD_CMD_EJECT_MEDIA,     do_eject_media },
+    { VOLD_CMD_FORMAT_MEDIA,    do_format_media },
+    { NULL, NULL }
+};
+
+int process_framework_command(int socket)
+{
+    int rc;
+    char buffer[101];
+
+    if ((rc = read(socket, buffer, sizeof(buffer) -1)) < 0) {
+        LOGE("Unable to read framework command (%s)", strerror(errno));
+        return -errno;
+    } else if (!rc)
+        return -ECONNRESET;
+
+    int start = 0;
+    int i;
+
+    buffer[rc] = 0;
+
+    for (i = 0; i < rc; i++) {
+        if (buffer[i] == 0) {
+            dispatch_cmd(buffer + start);
+            start = i + 1;
+        }
+    }
+    return 0;
+}
+
+static void dispatch_cmd(char *cmd)
+{
+    struct cmd_dispatch *c;
+
+    LOG_VOL("dispatch_cmd(%s):", cmd);
+
+    for (c = dispatch_table; c->cmd != NULL; c++) {
+        if (!strncmp(c->cmd, cmd, strlen(c->cmd))) {
+            c->dispatch(cmd);
+            return;
+        }
+    }
+
+    LOGE("No cmd handlers defined for '%s'", cmd);
+}
+
+static int do_send_ums_status(char *cmd)
+{
+    return ums_send_status();
+}
+
+static int do_set_ums_enable(char *cmd)
+{
+    if (!strcmp(cmd, VOLD_CMD_ENABLE_UMS))
+        return volmgr_enable_ums(true);
+
+    return volmgr_enable_ums(false);
+}
+
+static int do_mount_volume(char *cmd)
+{
+    return volmgr_start_volume_by_mountpoint(&cmd[strlen("mount_volume:")]);
+}
+
+static int do_format_media(char *cmd)
+{
+    return volmgr_format_volume(&cmd[strlen("format_media:")]);
+}
+
+static int do_eject_media(char *cmd)
+{
+    return volmgr_stop_volume_by_mountpoint(&cmd[strlen("eject_media:")]);
+}
diff --git a/vold/cmd_dispatch.h b/vold/cmd_dispatch.h
new file mode 100644
index 0000000..f8ba6b3
--- /dev/null
+++ b/vold/cmd_dispatch.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CMD_DISPATCH_H
+#define _CMD_DISPATCH_H
+
+// These must match the strings in java/android/android/os/UsbListener.java
+#define VOLD_CMD_ENABLE_UMS         "enable_ums"
+#define VOLD_CMD_DISABLE_UMS        "disable_ums"
+#define VOLD_CMD_SEND_UMS_STATUS    "send_ums_status"
+
+// these commands should contain a volume mount point after the colon
+#define VOLD_CMD_MOUNT_VOLUME       "mount_volume:"
+#define VOLD_CMD_EJECT_MEDIA        "eject_media:"
+#define VOLD_CMD_FORMAT_MEDIA       "format_media:"
+
+#endif
diff --git a/vold/devmapper.c b/vold/devmapper.c
new file mode 100644
index 0000000..ef44c90
--- /dev/null
+++ b/vold/devmapper.c
@@ -0,0 +1,463 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sched.h>
+#include <fcntl.h>
+
+#include <sys/mount.h>
+
+#include <linux/loop.h>
+#include <linux/dm-ioctl.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/properties.h>
+
+#include "vold.h"
+#include "devmapper.h"
+
+#define DEBUG_DEVMAPPER 1
+
+static void *_align(void *ptr, unsigned int a)
+{
+        register unsigned long agn = --a;
+
+        return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
+static struct dm_ioctl *_dm_ioctl_setup(struct devmapping *dm, int flags)
+{
+    void *buffer;
+    void *p;
+    const size_t min_size = 16 * 1024;
+    size_t len = sizeof(struct dm_ioctl);
+    struct dm_ioctl *io;
+    struct dm_target_spec *tgt;
+    int i;
+    char params[1024];
+    char key[80];
+
+    key[0] = '\0';
+    for (i = 0; i < (int) sizeof(dm->key); i++) {
+        char tmp[8];
+
+        sprintf(tmp, "%02x", dm->key[i]);
+        strcat(key, tmp);
+    }
+
+    char srcdev[128];
+
+    // XXX: Handle non crypt targets and non twofish (use param)
+    if (dm->src_type == dmsrc_loopback)
+        strcpy(srcdev, dm->type_data.loop.loop_dev);
+    else if (dm->src_type == dmsrc_partition)
+        strcpy(srcdev, dm->type_data.part.part_dev);
+
+    sprintf(params, "twofish %s 0 %s 0", key, srcdev);
+
+LOG_VOL("Params = '%s'", params);
+
+    if (len < min_size)
+        len = min_size;
+
+    if (!(buffer = malloc(len))) {
+        LOGE("out of memory");
+        return NULL;
+    }
+
+    memset(buffer, 0, len);
+    io = buffer;
+    tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+
+    io->data_size = len;
+    io->data_start = sizeof(struct dm_ioctl);
+
+    io->flags = flags;
+    io->dev = 0;
+
+    io->target_count = 1;
+    io->event_nr = 1;
+    strncpy(io->name, dm->target, sizeof(io->name));
+
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = (dm->size_mb * (1024 * 1024)) / 512;
+    strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type));
+
+    p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+    strcpy((char *) p, params);
+    p+= strlen(params) + 1;
+
+    p = _align(p, 8);
+    tgt->next = p - buffer;
+
+    return io;
+}
+
+static int get_next_available_dm()
+{
+    int i;
+
+    for (i = 0; i < 8; i++) {
+        char path[255];
+        sprintf(path, "/dev/block/dm-%d", i);
+        if ((access(path, F_OK) < 0) && (errno == ENOENT))
+            return i;
+    }
+
+    LOGE("Out of device mapper numbers");
+    return -1;
+}
+
+static int create_devmapping(struct devmapping *dm)
+{
+    struct dm_ioctl *io;
+    int rc, fd;
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("create_devmapping():");
+#endif
+
+    if (dm->dm_no < 0) {
+        LOGE("Invalid dm_no set");
+        return -EINVAL;
+    }
+
+    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOGE("Error opening device mapper (%d)", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(dm, 0))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        close(fd);
+        return -ENOMEM;
+    }
+
+    if ((rc = ioctl(fd, DM_DEV_CREATE, io)) < 0) {
+        LOGE("device-mapper create ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+    free(io);
+
+    if (!(io = _dm_ioctl_setup(dm, DM_STATUS_TABLE_FLAG))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(fd, DM_TABLE_LOAD, io)) < 0) {
+        LOGE("device-mapper load ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+    free(io);
+
+    if (!(io = _dm_ioctl_setup(dm, 0))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(fd, DM_DEV_SUSPEND, io)) < 0) {
+        LOGE("device-mapper resume ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+out_free:
+    free (io);
+out_nofree:
+    close (fd);
+    return rc;
+}
+
+static int loopback_start(struct devmapping *dm)
+{
+    int i;
+    int fd;
+    char filename[255];
+    int rc;
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("loopback_start(%s):", dm->type_data.loop.loop_src);
+#endif
+
+    for (i = 0; i < MAX_LOOP; i++) {
+        struct loop_info info;
+
+        sprintf(filename, "/dev/block/loop%d", i);
+
+        if ((fd = open(filename, O_RDWR)) < 0) {
+            LOGE("Unable to open %s (%s)", filename, strerror(errno));
+            return -errno;
+        }
+
+        rc = ioctl(fd, LOOP_GET_STATUS, &info);
+        if (rc < 0 && errno == ENXIO)
+            break;
+
+        close(fd);
+
+        if (rc < 0) {
+            LOGE("Unable to get loop status for %s (%s)", filename,
+                 strerror(errno));
+            return -errno;
+        }
+    }
+
+    if (i == MAX_LOOP) {
+        LOGE("Out of loop devices");
+        return -ENOSPC;
+    }
+
+    int file_fd;
+
+    if ((file_fd = open(dm->type_data.loop.loop_src, O_RDWR)) < 0) {
+        LOGE("Unable to open %s (%s)", dm->type_data.loop.loop_src,
+             strerror(errno));
+        return -errno;
+    }
+
+    if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
+        LOGE("Error setting up loopback interface (%s)", strerror(errno));
+        return -errno;
+    }
+
+    dm->type_data.loop.loop_dev = strdup(filename);
+    dm->type_data.loop.loop_no  = i;
+
+    close(fd);
+    close(file_fd);
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("Loop setup on %s for %s", dm->type_data.loop.loop_dev,
+            dm->type_data.loop.loop_src);
+#endif
+
+    return 0;
+}
+
+int devmapper_start(struct devmapping *dm)
+{
+    int rc;
+    char src_blkdev_path[255];
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("devmapper_start()");
+#endif
+
+    if (dm->src_type == dmsrc_loopback) {
+       if ((rc = loopback_start(dm)) < 0)
+           return rc;
+    } else if (dm->src_type == dmsrc_partition) {
+        LOGE("partition maps not yet supported");
+        return -ENOSYS;
+    } else {
+        LOGE("devmapper_start(): Unsupported source type '%d'", dm->src_type);
+        return -ENOENT;
+    }
+
+    /*
+     * Configure the device mapper
+     */
+    if ((rc = create_devmapping(dm)) < 0) {
+        LOGE("Failed to create devmapping (%d)", rc);
+        // XXX: if loopback then tear down
+        return rc;
+    }
+
+    return 0;
+}
+
+struct devmapping *devmapper_init(char *src, char *src_type, uint32_t size_mb,
+                  char *target, char *params, char *tgt_fs, char *mediapath)
+{
+    struct devmapping *dm;
+
+    if (!(dm = malloc(sizeof(struct devmapping)))) {
+        LOGE("devmapper_init(): out of memory");
+        return NULL;
+    }
+
+    memset(dm, 0, sizeof(struct devmapping));
+
+    if (!strcmp(src_type, "loopback_file")) {
+        dm->src_type = dmsrc_loopback;
+        dm->type_data.loop.loop_src = strdup(src);
+    } else if (!strncmp(src_type, "partition ", strlen("partition "))) {
+        dm->src_type = dmsrc_partition;
+        char *p = strtok(src_type, " ");
+        if (!p) {
+            LOGE("Invalid partition specifier");
+            goto out_free;
+        }
+        dm->type_data.part.part_type = strtoul(p, NULL, 0);
+    } else {
+        LOGE("Invalid src_type defined (%s)", src_type);
+        goto out_free;
+    }
+    
+    // XXX: Validate these
+    dm->size_mb = size_mb;
+    dm->target = strdup(target);
+    dm->params = strdup(params);
+    dm->tgt_fs = strdup(tgt_fs);
+    
+    if ((dm->dm_no = get_next_available_dm()) < 0)
+        goto out_free;
+
+    sprintf(mediapath, "/devices/virtual/block/dm-%d", dm->dm_no);
+
+    if (!(dm->media = media_create(mediapath,
+                                   "unknown",
+                                   "unknown",
+                                   media_devmapper))) {
+        LOGE("Unable to create media");
+        goto out_free;
+    }
+
+    return dm;
+ out_free:
+    if (dm->target)
+        free(dm->target);
+    if (dm->params)
+        free(dm->params);
+    if (dm->tgt_fs)
+        free(dm->tgt_fs);
+
+    free(dm);
+    return NULL;
+}
+
+int devmapper_genesis(struct devmapping *dm)
+{
+
+    if (dm->src_type == dmsrc_loopback) {
+        int fd;
+
+        LOG_VOL("devmapper_genesis(): Working on %s", 
+                dm->type_data.loop.loop_src);
+
+        unlink(dm->type_data.loop.loop_src);
+
+        LOG_VOL("devmapper_genesis(): Creating imagefile (%u MB)",
+                dm->size_mb);
+
+        if ((fd = creat(dm->type_data.loop.loop_src, 0600)) < 0) {
+            LOGE("Error creating imagefile (%s)", strerror(errno));
+            return -errno;
+        }
+
+        if (ftruncate(fd, (dm->size_mb * (1024 * 1024))) < 0) {
+            LOGE("Error truncating imagefile (%s)", strerror(errno));
+            close(fd);
+            return -errno;
+        }
+        close(fd);
+    } else if (dm->src_type == dmsrc_partition) {
+        LOGE("partition maps not yet supported");
+        return -ENOSYS;
+    }
+
+    return devmapper_start(dm);
+}
+
+static int destroy_devmapping(struct devmapping *dm)
+{
+    struct dm_ioctl       *io;
+    int                   dmFd;
+    int                   rc = 0;
+
+    LOG_VOL("destroy_devmapping():");
+
+    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOGE("Error opening device mapper (%d)", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(dm, DM_PERSISTENT_DEV_FLAG))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) {
+        LOGE("device-mapper remove ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+out_free:
+    free (io);
+out_nofree:
+    close (dmFd);
+    return rc;
+}
+
+static int loopback_stop(struct devmapping *dm)
+{
+    char devname[255];
+    int device_fd;
+    int rc = 0;
+
+    LOG_VOL("loopback_stop():");
+
+    device_fd = open(dm->type_data.loop.loop_dev, O_RDONLY);
+    if (device_fd < 0) {
+        LOG_ERROR("Failed to open loop (%d)", errno);
+        return -errno;
+    }
+
+    if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
+        LOG_ERROR("Failed to destroy loop (%d)", errno);
+        rc = -errno;
+    }
+
+    close(device_fd);
+    return rc;
+}
+
+int devmapper_stop(struct devmapping *dm)
+{
+    int rc;
+
+    LOG_VOL("devmapper_stop():");
+
+    if ((rc = destroy_devmapping(dm)))
+        return rc;
+
+    if (dm->src_type == dmsrc_loopback) {
+        if ((rc = loopback_stop(dm)))
+            return rc;
+    } else if (dm->src_type == dmsrc_partition) {
+        LOGE("partition maps not yet supported");
+        return -ENOSYS;
+    }
+    return 0;
+}
diff --git a/vold/devmapper.h b/vold/devmapper.h
new file mode 100644
index 0000000..3d8cab3
--- /dev/null
+++ b/vold/devmapper.h
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEVMAPPER_H
+#define _DEVMAPPER_H
+
+#include <pthread.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "media.h"
+
+#define MAX_LOOP 8
+
+enum dm_src_type {
+    dmsrc_unknown,
+    dmsrc_loopback,
+    dmsrc_partition,
+};
+
+struct loop_data {
+    char *loop_src;
+
+    char *loop_dev;
+    int  loop_no;
+};
+
+struct part_data {
+    char part_type;
+    
+    char *part_dev;
+};
+
+struct devmapping {
+        enum dm_src_type src_type;
+        union {
+            struct loop_data loop;
+            struct part_data part;
+        } type_data;
+        
+        uint32_t         size_mb;
+	char             *target;
+        char             *params;
+        char             *tgt_fs;
+
+        unsigned char key[16];
+        int           dm_no;
+
+        media_t *media;
+};
+
+struct devmapping *devmapper_init(char *, char *, unsigned int, char *, char *, char *, char *);
+int devmapper_start(struct devmapping *);
+int devmapper_stop(struct devmapping *);
+int devmapper_genesis(struct devmapping *);
+#endif
diff --git a/vold/diskmbr.h b/vold/diskmbr.h
new file mode 100644
index 0000000..417e7ca
--- /dev/null
+++ b/vold/diskmbr.h
@@ -0,0 +1,75 @@
+/*-
+ * 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
+
+void dos_partition_dec(void const *pp, struct dos_partition *d);
+void dos_partition_enc(void *pp, struct dos_partition *d);
+
+#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/vold/format.c b/vold/format.c
new file mode 100755
index 0000000..dd0515c
--- /dev/null
+++ b/vold/format.c
@@ -0,0 +1,113 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include <linux/fs.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "format.h"
+#include "diskmbr.h"
+#include "logwrapper.h"
+
+static char MKDOSFS_PATH[] = "/system/bin/mkdosfs";
+static char MKE2FS_PATH[] = "/system/bin/mke2fs";
+
+int format_partition(blkdev_t *part, char *type)
+{
+    char *devpath;
+    int rc = -EINVAL;
+
+    devpath = blkdev_get_devpath(part);
+
+    if (!strcmp(type, FORMAT_TYPE_FAT32)) {
+        char *args[7];
+        args[0] = MKDOSFS_PATH;
+        args[1] = "-F 32";
+        args[2] = "-c 32";
+        args[3] = "-n 2";
+        args[4] = "-O android";
+        args[5] = devpath;
+        args[6] = NULL;
+        rc = logwrap(6, args);
+    } else {
+        char *args[7];
+        args[0] = MKE2FS_PATH;
+        args[1] = "-b 4096";
+        args[2] = "-m 1";
+        args[3] = "-L android";
+        args[4] = "-v";
+        args[5] = devpath;
+        args[6] = NULL;
+        rc = logwrap(6, args);
+    }
+ 
+    free(devpath);
+
+    if (rc == 0) {
+        LOG_VOL("Filesystem formatted OK");
+        return 0;
+    } else {
+        LOGE("Format failed (unknokwn exit code %d)", rc);
+        return -EIO;
+    }
+    return 0;
+}
+
+int initialize_mbr(blkdev_t *disk)
+{
+    int fd, rc;
+    unsigned char block[512];
+    struct dos_partition part;
+    char *devpath;
+
+    devpath = blkdev_get_devpath(disk);
+
+    memset(&part, 0, sizeof(part));
+    part.dp_flag = 0x80;
+    part.dp_typ = 0xc;
+    part.dp_start = ((1024 * 64) / 512) + 1;
+    part.dp_size = disk->nr_sec - part.dp_start;
+
+    memset(block, 0, sizeof(block));
+    block[0x1fe] = 0x55;
+    block[0x1ff] = 0xaa;
+
+    dos_partition_enc(block + DOSPARTOFF, &part);
+
+    if ((fd = open(devpath, O_RDWR)) < 0) {
+        LOGE("Error opening disk file (%s)", strerror(errno));
+        return -errno;
+    }
+    free(devpath);
+
+    if (write(fd, block, sizeof(block)) < 0) {
+        LOGE("Error writing MBR (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+
+    if (ioctl(fd, BLKRRPART, NULL) < 0) {
+        LOGE("Error re-reading partition table (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    close(fd);
+    return 0;
+}
diff --git a/vold/format.h b/vold/format.h
new file mode 100644
index 0000000..73cc012
--- /dev/null
+++ b/vold/format.h
@@ -0,0 +1,26 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FORMAT_H
+#define _FORMAT_H
+
+#define FORMAT_TYPE_EXT2  "ext2"
+#define FORMAT_TYPE_FAT32 "fat32"
+
+int format_partition(blkdev_t *part, char *type);
+int initialize_mbr(blkdev_t *disk);
+#endif
diff --git a/vold/geom_mbr_enc.c b/vold/geom_mbr_enc.c
new file mode 100644
index 0000000..f1f8339
--- /dev/null
+++ b/vold/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/vold/logwrapper.c b/vold/logwrapper.c
new file mode 100644
index 0000000..25d2281
--- /dev/null
+++ b/vold/logwrapper.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+
+int parent(const char *tag, int parent_read) {
+    int status;
+    char buffer[4096];
+
+    int a = 0;  // start index of unprocessed data
+    int b = 0;  // end index of unprocessed data
+    int sz;
+    while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
+
+        sz += b;
+        // Log one line at a time
+        for (b = 0; b < sz; b++) {
+            if (buffer[b] == '\r') {
+                buffer[b] = '\0';
+            } else if (buffer[b] == '\n') {
+                buffer[b] = '\0';
+                LOG(LOG_INFO, tag, &buffer[a]);
+                a = b + 1;
+            }
+        }
+
+        if (a == 0 && b == sizeof(buffer) - 1) {
+            // buffer is full, flush
+            buffer[b] = '\0';
+            LOG(LOG_INFO, tag, &buffer[a]);
+            b = 0;
+        } else if (a != b) {
+            // Keep left-overs
+            b -= a;
+            memmove(buffer, &buffer[a], b);
+            a = 0;
+        } else {
+            a = 0;
+            b = 0;
+        }
+
+    }
+    // Flush remaining data
+    if (a != b) {
+        buffer[b] = '\0';
+        LOG(LOG_INFO, tag, &buffer[a]);
+    }
+    status = 0xAAAA;
+    if (wait(&status) != -1) {  // Wait for child
+        if (WIFEXITED(status)) {
+            LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
+                    WEXITSTATUS(status));
+            return WEXITSTATUS(status);
+        } else if (WIFSIGNALED(status))
+            LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
+                    WTERMSIG(status));
+        else if (WIFSTOPPED(status))
+            LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
+                    WSTOPSIG(status));
+    } else
+        LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
+                strerror(errno), errno);
+    return -EAGAIN;
+}
+
+void child(int argc, char* argv[]) {
+    // create null terminated argv_child array
+    char* argv_child[argc + 1];
+    memcpy(argv_child, argv, argc * sizeof(char *));
+    argv_child[argc] = NULL;
+
+    // XXX: PROTECT FROM VIKING KILLER
+    if (execvp(argv_child[0], argv_child)) {
+        LOG(LOG_ERROR, "logwrapper",
+            "executing %s failed: %s", argv_child[0], strerror(errno));
+        exit(-1);
+    }
+}
+
+int logwrap(int argc, char* argv[], pid_t *childPid)
+{
+    pid_t pid;
+
+    int parent_ptty;
+    int child_ptty;
+    char *child_devname = NULL;
+
+    /* Use ptty instead of socketpair so that STDOUT is not buffered */
+    parent_ptty = open("/dev/ptmx", O_RDWR);
+    if (parent_ptty < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty");
+	return -errno;
+    }
+
+    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+	LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx");
+	return -1;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Failed to fork");
+        return -errno;
+    } else if (pid == 0) {
+        child_ptty = open(child_devname, O_RDWR);
+        if (child_ptty < 0) {
+	    LOG(LOG_ERROR, "logwrapper", "Problem with child ptty");
+            return -errno;
+        }
+
+        // redirect stdout and stderr
+        close(parent_ptty);
+        dup2(child_ptty, 1);
+        dup2(child_ptty, 2);
+        close(child_ptty);
+
+        child(argc, argv);
+    } else {
+        return parent(argv[0], parent_ptty);
+    }
+
+    return 0;
+}
diff --git a/vold/logwrapper.h b/vold/logwrapper.h
new file mode 100644
index 0000000..602e24c
--- /dev/null
+++ b/vold/logwrapper.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGWRAPPER_H
+#define _LOGWRAPPER_H
+
+#include <stdlib.h>
+int logwrap(int argc, char* argv[]);
+#endif
diff --git a/vold/media.c b/vold/media.c
new file mode 100644
index 0000000..40637ff
--- /dev/null
+++ b/vold/media.c
@@ -0,0 +1,165 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "vold.h"
+#include "media.h"
+
+static media_list_t *list_root = NULL;
+
+media_t *media_create(char *devpath, char *name, char *serial, media_type_t type)
+{
+    media_list_t *list_entry;
+    media_t *new;
+
+    if (!(new = malloc(sizeof(media_t))))
+        return NULL;
+
+    memset(new, 0, sizeof(media_t));
+
+    if (!(list_entry = malloc(sizeof(media_list_t)))) {
+        free(new);
+        return NULL;
+    }
+    list_entry->media = new;
+    list_entry->next = NULL;
+
+    if (!list_root)
+        list_root = list_entry;
+    else {
+        media_list_t *list_scan = list_root;
+        while(list_scan->next)
+            list_scan = list_scan->next;
+        list_scan->next = list_entry;
+    }
+     
+    new->devpath = strdup(devpath);
+    new->name = strdup(name);
+    if (!serial)
+        new->serial = 0;
+    else
+        new->serial = strtoul(serial, NULL, 0);
+
+    new->media_type = type;
+
+    return new;
+}
+
+void media_destroy(media_t *media)
+{
+    media_list_t *list_next;
+
+    if (list_root->media == media) {
+        list_next = list_root->next;
+        free(list_root);
+        list_root = list_next;
+    } else {
+        media_list_t *list_scan = list_root;
+        while (list_scan->next->media != media)
+            list_scan = list_scan -> next;
+        list_next = list_scan->next->next;
+        free(list_scan->next);
+        list_scan->next = list_next;
+    }
+
+    free(media->devpath);
+    free(media->name);
+
+    while(media->devs)
+        media_remove_blkdev(media, media->devs->dev);
+    free(media);
+}
+
+media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match)
+{
+    media_list_t *list_scan = list_root;
+
+    while (list_scan) {
+        if (fuzzy_match) {
+            if (!strncmp(list_scan->media->devpath, devpath, strlen(devpath)))
+                return list_scan->media;
+        } else {
+            if (!strcmp(list_scan->media->devpath, devpath))
+                return list_scan->media;
+        }
+        list_scan = list_scan->next;
+    }
+#if DEBUG_MEDIA
+    LOG_VOL("media_lookup_by_path(): No media found @ %s", devpath);
+#endif
+    return NULL;
+}
+
+int media_add_blkdev(media_t *card, blkdev_t *dev)
+{
+    blkdev_list_t *list_entry;
+
+    if (!(list_entry = malloc(sizeof(blkdev_list_t)))) {
+        LOGE("Out of memory");
+        return -ENOMEM;
+    }
+    
+    list_entry->next = NULL;
+    list_entry->dev = dev;
+    if (!card->devs)
+        card->devs = list_entry;
+    else {
+        blkdev_list_t *scan = card->devs;
+
+        while(scan->next)
+            scan = scan->next;
+
+        scan->next = list_entry;
+    }
+    return 0;
+}
+
+void media_remove_blkdev(media_t *card, blkdev_t *dev)
+{
+    if (card->devs->dev == dev)
+        card->devs = card->devs->next;
+    else {
+        blkdev_list_t *scan = card->devs;
+        while (scan->next->dev != dev)
+            scan = scan -> next;
+        blkdev_list_t *next = scan->next->next;
+        free(scan->next);
+        scan->next = next;
+    }
+}
+
+media_t *media_lookup_by_dev(blkdev_t *dev)
+{
+    media_list_t *media_scan = list_root;
+
+    while (media_scan) {
+        blkdev_list_t *blk_scan = media_scan->media->devs;
+        while (blk_scan) {
+            if (blk_scan->dev == dev)
+                return media_scan->media;
+            blk_scan = blk_scan->next;
+        }
+        media_scan = media_scan->next;
+    }
+    return NULL;
+}
diff --git a/vold/media.h b/vold/media.h
new file mode 100644
index 0000000..567ce04
--- /dev/null
+++ b/vold/media.h
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MEDIA_H
+#define _MEDIA_H
+
+#include <sys/types.h>
+
+#include "blkdev.h"
+
+typedef enum media_type {
+    media_unknown,
+    media_mmc,
+    media_devmapper,
+} media_type_t;
+
+typedef struct media {
+    char           *devpath;
+    char           *name;
+    uint32_t       serial;
+    media_type_t   media_type;
+
+    blkdev_list_t  *devs;
+} media_t;
+
+typedef struct media_list {
+    media_t           *media;
+    struct media_list *next;
+} media_list_t;
+
+media_t *media_create(char *devpath, char *name, char *serial, enum media_type);
+media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match);
+media_t *media_lookup_by_dev(blkdev_t *dev);
+void media_destroy(media_t *media);
+int media_add_blkdev(media_t *media, blkdev_t *dev);
+void media_remove_blkdev(media_t *media, blkdev_t *dev);
+#endif
diff --git a/vold/misc.c b/vold/misc.c
new file mode 100644
index 0000000..951414c
--- /dev/null
+++ b/vold/misc.c
@@ -0,0 +1,91 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void *read_file(char *filename, ssize_t *_size)
+{
+	int ret, fd;
+	struct stat sb;
+	ssize_t size;
+	void *buffer = NULL;
+
+	/* open the file */
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* find out how big it is */
+	if (fstat(fd, &sb) < 0)
+		goto bail;
+	size = sb.st_size;
+
+	/* allocate memory for it to be read into */
+	buffer = malloc(size);
+	if (!buffer)
+		goto bail;
+
+	/* slurp it into our buffer */
+	ret = read(fd, buffer, size);
+	if (ret != size)
+		goto bail;
+
+	/* let the caller know how big it is */
+	*_size = size;
+
+bail:
+	close(fd);
+	return buffer;
+}
+char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer)
+{
+    int i;
+
+    strcpy(buffer, path);
+
+    for (i = 0; i < num_elements_to_remove; i++) {
+        char *p = &buffer[strlen(buffer)-1];
+
+        for (p = &buffer[strlen(buffer) -1]; *p != '/'; p--);
+        *p = '\0';
+    }
+
+    return buffer;
+}
+
+char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var)
+{
+    char filename[255];
+    char *p;
+    ssize_t sz;
+
+    sprintf(filename, "/sys%s/%s", devpath, var);
+    p = read_file(filename, &sz);
+    p[(strlen(p) - 1)] = '\0';
+    strncpy(buffer, p, maxlen);
+    free(p);
+    return buffer;
+}
+
diff --git a/vold/mmc.c b/vold/mmc.c
new file mode 100644
index 0000000..0f08964
--- /dev/null
+++ b/vold/mmc.c
@@ -0,0 +1,293 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "vold.h"
+#include "mmc.h"
+#include "media.h"
+
+#define DEBUG_BOOTSTRAP 0
+
+static int mmc_bootstrap_controller(char *sysfs_path);
+static int mmc_bootstrap_card(char *sysfs_path);
+static int mmc_bootstrap_block(char *devpath);
+static int mmc_bootstrap_mmcblk(char *devpath);
+static int mmc_bootstrap_mmcblk_partition(char *devpath);
+
+/*
+ * Bootstrap our mmc information.
+ */
+int mmc_bootstrap()
+{
+    DIR *d;
+    struct dirent *de;
+
+    if (!(d = opendir(SYSFS_CLASS_MMC_PATH))) {
+        LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_MMC_PATH);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+
+        sprintf(tmp, "%s/%s", SYSFS_CLASS_MMC_PATH, de->d_name);
+        if (mmc_bootstrap_controller(tmp))
+            LOG_ERROR("Error bootstrapping controller '%s' (%m)", tmp);
+    }
+
+    closedir(d);
+
+    return 0;
+}
+
+static int mmc_bootstrap_controller(char *sysfs_path)
+{
+    DIR *d;
+    struct dirent *de;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("bootstrap_controller(%s):", sysfs_path);
+#endif
+    if (!(d = opendir(sysfs_path))) {
+        LOG_ERROR("Unable to open '%s' (%m)", sysfs_path);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+
+        if ((!strcmp(de->d_name, "uevent")) ||
+            (!strcmp(de->d_name, "subsystem")) ||
+            (!strcmp(de->d_name, "device")) ||
+            (!strcmp(de->d_name, "power"))) {
+            continue;
+        }
+
+        sprintf(tmp, "%s/%s", sysfs_path, de->d_name);
+
+        if (mmc_bootstrap_card(tmp) < 0)
+            LOG_ERROR("Error bootstrapping card '%s' (%m)", tmp);
+    } // while
+
+    closedir(d);
+    return 0;   
+}
+
+static int mmc_bootstrap_card(char *sysfs_path)
+{
+    char saved_cwd[255];
+    char new_cwd[255];
+    char *devpath;
+    char *uevent_params[4];
+    char *p;
+    char filename[255];
+    char tmp[255];
+    ssize_t sz;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("bootstrap_card(%s):", sysfs_path);
+#endif
+
+    /*
+     * sysfs_path is based on /sys/class, but we want the actual device class
+     */
+    if (!getcwd(saved_cwd, sizeof(saved_cwd))) {
+        LOGE("Error getting working dir path");
+        return -errno;
+    }
+    
+    if (chdir(sysfs_path) < 0) {
+        LOGE("Unable to chdir to %s (%m)", sysfs_path);
+        return -errno;
+    }
+
+    if (!getcwd(new_cwd, sizeof(new_cwd))) {
+        LOGE("Buffer too small for device path");
+        return -errno;
+    }
+
+    if (chdir(saved_cwd) < 0) {
+        LOGE("Unable to restore working dir");
+        return -errno;
+    }
+
+    devpath = &new_cwd[4]; // Skip over '/sys'
+
+    /*
+     * Collect parameters so we can simulate a UEVENT
+     */
+    sprintf(tmp, "DEVPATH=%s", devpath);
+    uevent_params[0] = (char *) strdup(tmp);
+
+    sprintf(filename, "/sys%s/type", devpath);
+    p = read_file(filename, &sz);
+    p[strlen(p) - 1] = '\0';
+    sprintf(tmp, "MMC_TYPE=%s", p);
+    free(p);
+    uevent_params[1] = (char *) strdup(tmp);
+
+    sprintf(filename, "/sys%s/name", devpath);
+    p = read_file(filename, &sz);
+    p[strlen(p) - 1] = '\0';
+    sprintf(tmp, "MMC_NAME=%s", p);
+    free(p);
+    uevent_params[2] = (char *) strdup(tmp);
+
+    uevent_params[3] = (char *) NULL;
+
+    if (simulate_uevent("mmc", devpath, "add", uevent_params) < 0) {
+        LOGE("Error simulating uevent (%m)");
+        return -errno;
+    }
+
+    /*
+     *  Check for block drivers
+     */
+    char block_devpath[255];
+    sprintf(tmp, "%s/block", devpath);
+    sprintf(filename, "/sys%s/block", devpath);
+    if (!access(filename, F_OK)) {
+        if (mmc_bootstrap_block(tmp)) {
+            LOGE("Error bootstrapping block @ %s", tmp);
+        }
+    }
+
+    return 0;
+}
+
+static int mmc_bootstrap_block(char *devpath)
+{
+    char blockdir_path[255];
+    DIR *d;
+    struct dirent *de;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("mmc_bootstrap_block(%s):", devpath);
+#endif
+
+    sprintf(blockdir_path, "/sys%s", devpath);
+
+    if (!(d = opendir(blockdir_path))) {
+        LOGE("Failed to opendir %s", devpath);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+        sprintf(tmp, "%s/%s", devpath, de->d_name);
+        if (mmc_bootstrap_mmcblk(tmp))
+            LOGE("Error bootstraping mmcblk @ %s", tmp);
+    }
+    closedir(d);
+    return 0;
+}
+
+static int mmc_bootstrap_mmcblk(char *devpath)
+{
+    char *mmcblk_devname;
+    int part_no;
+    int rc;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("mmc_bootstrap_mmcblk(%s):", devpath);
+#endif
+
+    if ((rc = mmc_bootstrap_mmcblk_partition(devpath))) {
+        LOGE("Error bootstrapping mmcblk partition '%s'", devpath);
+        return rc;
+    }
+
+    for (mmcblk_devname = &devpath[strlen(devpath)];
+         *mmcblk_devname != '/'; mmcblk_devname--);
+    mmcblk_devname++;
+
+    for (part_no = 0; part_no < 4; part_no++) {
+        char part_file[255];
+        sprintf(part_file, "/sys%s/%sp%d", devpath, mmcblk_devname, part_no);
+        if (!access(part_file, F_OK)) {
+            char part_devpath[255];
+
+            sprintf(part_devpath, "%s/%sp%d", devpath, mmcblk_devname, part_no);
+            if (mmc_bootstrap_mmcblk_partition(part_devpath)) 
+                LOGE("Error bootstrapping mmcblk partition '%s'", part_devpath);
+        }
+    }
+
+    return 0;
+}
+
+static int mmc_bootstrap_mmcblk_partition(char *devpath)
+{
+    char filename[255];
+    char *uevent_buffer;
+    ssize_t sz;
+    char *uevent_params[4];
+    char tmp[255];
+    FILE *fp;
+    char line[255];
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("mmc_bootstrap_mmcblk_partition(%s):", devpath);
+#endif
+
+    sprintf(tmp, "DEVPATH=%s", devpath);
+    uevent_params[0] = strdup(tmp);
+
+    sprintf(filename, "/sys%s/uevent", devpath);
+    if (!(fp = fopen(filename, "r"))) {
+        LOGE("Unable to open '%s' (%m)", filename);
+        return -errno;
+    }
+
+    while (fgets(line, sizeof(line), fp)) {
+        line[strlen(line)-1] = 0;
+        if (!strncmp(line, "DEVTYPE=", 8))
+            uevent_params[1] = strdup(line);
+        else if (!strncmp(line, "MAJOR=",6)) 
+            uevent_params[2] = strdup(line);
+        else if (!strncmp(line, "MINOR=",6)) 
+            uevent_params[3] = strdup(line);
+    }
+    fclose(fp);
+
+    if (!uevent_params[1] || !uevent_params[2] || !uevent_params[3]) {
+        LOGE("mmcblk uevent missing required params");
+        return -1;
+    }
+    uevent_params[4] = '\0';
+    
+    if (simulate_uevent("block", devpath, "add", uevent_params) < 0) {
+        LOGE("Error simulating uevent (%m)");
+        return -errno;
+    }
+    return 0;
+}
diff --git a/vold/mmc.h b/vold/mmc.h
new file mode 100644
index 0000000..5a5d184
--- /dev/null
+++ b/vold/mmc.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MMC_H
+#define _MMC_H
+
+#define SYSFS_CLASS_MMC_PATH "/sys/class/mmc_host"
+
+#endif
diff --git a/vold/switch.c b/vold/switch.c
new file mode 100644
index 0000000..ba9ddb3
--- /dev/null
+++ b/vold/switch.c
@@ -0,0 +1,121 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "vold.h"
+#include "switch.h"
+
+#define DEBUG_BOOTSTRAP 0
+
+static int mmc_bootstrap_switch(char *sysfs_path);
+
+int switch_bootstrap()
+{
+    DIR *d;
+    struct dirent *de;
+
+    if (!(d = opendir(SYSFS_CLASS_SWITCH_PATH))) {
+        LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_SWITCH_PATH);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+
+        sprintf(tmp, "%s/%s", SYSFS_CLASS_SWITCH_PATH, de->d_name);
+        if (mmc_bootstrap_switch(tmp))
+            LOG_ERROR("Error bootstrapping switch '%s' (%m)", tmp);
+    }
+
+    closedir(d);
+
+    return 0;
+}
+
+static int mmc_bootstrap_switch(char *sysfs_path)
+{
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("bootstrap_switch(%s):", sysfs_path);
+#endif
+
+    char filename[255];
+    char name[255];
+    char state[255];
+    char tmp[255];
+    char *uevent_params[3];
+    char devpath[255];
+    FILE *fp;
+
+    /*
+     * Read switch name
+     */
+    sprintf(filename, "%s/name", sysfs_path);
+    if (!(fp = fopen(filename, "r"))) {
+        LOGE("Error opening switch name path '%s' (%s)",
+             sysfs_path, strerror(errno));
+       return -errno;
+    }
+    if (!fgets(name, sizeof(name), fp)) {
+        LOGE("Unable to read switch name");
+        fclose(fp);
+        return -EIO;
+    }
+    fclose(fp);
+
+    name[strlen(name) -1] = '\0';
+    sprintf(devpath, "/devices/virtual/switch/%s", name);
+    sprintf(tmp, "SWITCH_NAME=%s", name);
+    uevent_params[0] = (char *) strdup(tmp);
+
+    /*
+     * Read switch state
+     */
+    sprintf(filename, "%s/state", sysfs_path);
+    if (!(fp = fopen(filename, "r"))) {
+        LOGE("Error opening switch state path '%s' (%s)",
+             sysfs_path, strerror(errno));
+       return -errno;
+    }
+    if (!fgets(state, sizeof(state), fp)) {
+        LOGE("Unable to read switch state");
+        fclose(fp);
+        return -EIO;
+    }
+    fclose(fp);
+
+    state[strlen(state) -1] = '\0';
+    sprintf(tmp, "SWITCH_STATE=%s", state);
+    uevent_params[1] = (char *) strdup(tmp);
+
+    uevent_params[2] = (char *) NULL;
+
+    if (simulate_uevent("switch", devpath, "add", uevent_params) < 0) {
+        LOGE("Error simulating uevent (%s)", strerror(errno));
+        return -errno;
+    }
+
+    return 0;   
+}
diff --git a/vold/switch.h b/vold/switch.h
new file mode 100644
index 0000000..6729f2d
--- /dev/null
+++ b/vold/switch.h
@@ -0,0 +1,25 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SWITCH_H
+#define _SWITCH_H
+
+#include "vold.h"
+
+#define SYSFS_CLASS_SWITCH_PATH "/sys/class/switch"
+
+#endif
diff --git a/vold/uevent.c b/vold/uevent.c
new file mode 100644
index 0000000..b1a6944
--- /dev/null
+++ b/vold/uevent.c
@@ -0,0 +1,443 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "vold.h"
+#include "uevent.h"
+#include "mmc.h"
+#include "blkdev.h"
+#include "volmgr.h"
+#include "media.h"
+
+#define DEBUG_UEVENT 0
+
+#define UEVENT_PARAMS_MAX 32
+
+enum uevent_action { action_add, action_remove, action_change };
+
+struct uevent {
+    char *path;
+    enum uevent_action action;
+    char *subsystem;
+    char *param[UEVENT_PARAMS_MAX];
+    unsigned int seqnum;
+};
+
+struct uevent_dispatch {
+    char *subsystem;
+    int (* dispatch) (struct uevent *);
+};
+
+static void dump_uevent(struct uevent *);
+static int dispatch_uevent(struct uevent *event);
+static void free_uevent(struct uevent *event);
+static char *get_uevent_param(struct uevent *event, char *param_name);
+
+static int handle_powersupply_event(struct uevent *event);
+static int handle_switch_event(struct uevent *);
+static int handle_battery_event(struct uevent *);
+static int handle_mmc_event(struct uevent *);
+static int handle_block_event(struct uevent *);
+static int handle_bdi_event(struct uevent *);
+static void _cb_blkdev_ok_to_destroy(blkdev_t *dev);
+
+static struct uevent_dispatch dispatch_table[] = {
+    { "switch", handle_switch_event }, 
+    { "battery", handle_battery_event }, 
+    { "mmc", handle_mmc_event },
+    { "block", handle_block_event },
+    { "bdi", handle_bdi_event },
+    { "power_supply", handle_powersupply_event },
+    { NULL, NULL }
+};
+
+static boolean low_batt = false;
+static boolean door_open = true;
+
+int process_uevent_message(int socket)
+{
+    char buffer[64 * 1024]; // Thank god we're not in the kernel :)
+    int count;
+    char *s = buffer;
+    char *end;
+    struct uevent *event;
+    int param_idx = 0;
+    int i;
+    int first = 1;
+    int rc = 0;
+
+    if ((count = recv(socket, buffer, sizeof(buffer), 0)) < 0) {
+        LOGE("Error receiving uevent (%s)", strerror(errno));
+        return -errno;
+    }
+
+    if (!(event = malloc(sizeof(struct uevent)))) {
+        LOGE("Error allocating memory (%s)", strerror(errno));
+        return -errno;
+    }
+
+    memset(event, 0, sizeof(struct uevent));
+
+    end = s + count;
+    while (s < end) {
+        if (first) {
+            char *p;
+            for (p = s; *p != '@'; p++);
+            p++;
+            event->path = strdup(p);
+            first = 0;
+        } else {
+            if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
+                char *a = s + strlen("ACTION=");
+               
+                if (!strcmp(a, "add"))
+                    event->action = action_add;
+                else if (!strcmp(a, "change"))
+                    event->action = action_change;
+                else if (!strcmp(a, "remove"))
+                    event->action = action_remove;
+            } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
+                event->seqnum = atoi(s + strlen("SEQNUM="));
+            else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
+                event->subsystem = strdup(s + strlen("SUBSYSTEM="));
+            else
+                event->param[param_idx++] = strdup(s);
+        }
+        s+= strlen(s) + 1;
+    }
+
+    rc = dispatch_uevent(event);
+    
+    free_uevent(event);
+    return rc;
+}
+
+int simulate_uevent(char *subsys, char *path, char *action, char **params)
+{
+    struct uevent *event;
+    char tmp[255];
+    int i, rc;
+
+    if (!(event = malloc(sizeof(struct uevent)))) {
+        LOGE("Error allocating memory (%s)", strerror(errno));
+        return -errno;
+    }
+
+    memset(event, 0, sizeof(struct uevent));
+
+    event->subsystem = strdup(subsys);
+
+    if (!strcmp(action, "add"))
+        event->action = action_add;
+    else if (!strcmp(action, "change"))
+        event->action = action_change;
+    else if (!strcmp(action, "remove"))
+        event->action = action_remove;
+    else {
+        LOGE("Invalid action '%s'", action);
+        return -1;
+    }
+
+    event->path = strdup(path);
+
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!params[i])
+            break;
+        event->param[i] = strdup(params[i]);
+    }
+
+    rc = dispatch_uevent(event);
+    free_uevent(event);
+    return rc;
+}
+
+static int dispatch_uevent(struct uevent *event)
+{
+    int i;
+
+#if DEBUG_UEVENT
+    dump_uevent(event);
+#endif
+    for (i = 0; dispatch_table[i].subsystem != NULL; i++) {
+        if (!strcmp(dispatch_table[i].subsystem, event->subsystem))
+            return dispatch_table[i].dispatch(event);
+    }
+
+#if DEBUG_UEVENT
+    LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem);
+#endif
+    return 0;
+}
+
+static void dump_uevent(struct uevent *event)
+{
+    int i;
+
+    LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s",
+              event->seqnum, event->subsystem, event->action, event->path);
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!event->param[i])
+            break;
+        LOG_VOL("%s", event->param[i]);
+    }
+}
+
+static void free_uevent(struct uevent *event)
+{
+    int i;
+    free(event->path);
+    free(event->subsystem);
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!event->param[i])
+            break;
+        free(event->param[i]);
+    }
+    free(event);
+}
+
+static char *get_uevent_param(struct uevent *event, char *param_name)
+{
+    int i;
+
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!event->param[i])
+            break;
+        if (!strncmp(event->param[i], param_name, strlen(param_name)))
+            return &event->param[i][strlen(param_name) + 1];
+    }
+
+    LOGE("get_uevent_param(): No parameter '%s' found", param_name);
+    return NULL;
+}
+
+/*
+ * ---------------
+ * Uevent Handlers
+ * ---------------
+ */
+
+static int handle_powersupply_event(struct uevent *event)
+{
+    char *ps_type = get_uevent_param(event, "POWER_SUPPLY_TYPE");
+    char *ps_cap = get_uevent_param(event, "POWER_SUPPLY_CAPACITY");
+
+    if (!strcasecmp(ps_type, "battery")) {
+        int capacity = atoi(ps_cap);
+  
+        if (capacity < 5)
+            low_batt = true;
+        else
+            low_batt = false;
+LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open);
+        volmgr_safe_mode(low_batt || door_open);
+    }
+    return 0;
+}
+
+static int handle_switch_event(struct uevent *event)
+{
+    char *name = get_uevent_param(event, "SWITCH_NAME");
+    char *state = get_uevent_param(event, "SWITCH_STATE");
+
+
+    if (!strcmp(name, "usb_mass_storage")) {
+        if (!strcmp(state, "online")) {
+            ums_hostconnected_set(true);
+        } else {
+            ums_hostconnected_set(false);
+            volmgr_enable_ums(false);
+        }
+    } else if (!strcmp(name, "sd-door")) {
+        if (!strcmp(state, "open"))
+            door_open = true;
+        else
+            door_open = false;
+LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open);
+        volmgr_safe_mode(low_batt || door_open);
+    } else
+        LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name);
+
+    return 0;
+}
+
+static int handle_battery_event(struct uevent *event)
+{
+    return 0;
+}
+
+static int handle_block_event(struct uevent *event)
+{
+    char mediapath[255];
+    media_t *media;
+    int n;
+    int maj, min;
+    blkdev_t *blkdev;
+
+    /*
+     * Look for backing media for this block device
+     */
+    if (!strncmp(get_uevent_param(event, "DEVPATH"),
+                 "/devices/virtual/",
+                 strlen("/devices/virtual/"))) {
+        n = 0;
+    } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
+        n = 2;
+    else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
+        n = 3;
+    else {
+        LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE"));
+        return -EINVAL;
+    }
+
+    truncate_sysfs_path(event->path, n, mediapath);
+
+    if (!(media = media_lookup_by_path(mediapath, false))) {
+#if DEBUG_UEVENT
+        LOG_VOL("No backend media found @ device path '%s'", mediapath);
+#endif
+        return 0;
+    }
+
+    maj = atoi(get_uevent_param(event, "MAJOR"));
+    min = atoi(get_uevent_param(event, "MINOR"));
+
+    if (event->action == action_add) {
+        blkdev_t *disk;
+
+        /*
+         * If there isn't a disk already its because *we*
+         * are the disk
+         */
+        disk = blkdev_lookup_by_devno(maj, 0);
+
+        if (!(blkdev = blkdev_create(disk,
+                                     event->path,
+                                     maj,
+                                     min,
+                                     media,
+                                     get_uevent_param(event, "DEVTYPE")))) {
+            LOGE("Unable to allocate new blkdev (%m)");
+            return -1;
+        }
+
+        blkdev_refresh(blkdev);
+
+        /*
+         * Add the blkdev to media
+         */
+        int rc;
+        if ((rc = media_add_blkdev(media, blkdev)) < 0) {
+            LOGE("Unable to add blkdev to card (%d)", rc);
+            return rc;
+        }
+
+        LOG_VOL("New blkdev %d.%d on media %s, media path %s, Dpp %d",
+                blkdev->major, blkdev->minor, media->name, mediapath,
+                blkdev_get_num_pending_partitions(blkdev->disk));
+
+        if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
+            if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
+                LOGE("Volmgr failed to handle device (%d)", rc);
+                return rc;
+            }
+        }
+    } else if (event->action == action_remove) {
+        if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
+            return 0;
+
+        LOG_VOL("Destroying blkdev %d.%d @ %s on media %s", blkdev->major,
+                blkdev->minor, blkdev->devpath, media->name);
+        volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy);
+
+    } else if (event->action == action_change) {
+        if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
+            return 0;
+
+        LOG_VOL("Modified blkdev %d.%d @ %s on media %s", blkdev->major,
+                blkdev->minor, blkdev->devpath, media->name);
+        
+        blkdev_refresh(blkdev);
+    } else  {
+#if DEBUG_UEVENT
+        LOG_VOL("No handler implemented for action %d", event->action);
+#endif
+    }
+    return 0;
+}
+
+static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
+{
+    media_t *media = media_lookup_by_dev(dev);
+    if (media)
+        media_remove_blkdev(media, dev);
+    blkdev_destroy(dev);
+}
+
+static int handle_bdi_event(struct uevent *event)
+{
+    return 0;
+}
+
+static int handle_mmc_event(struct uevent *event)
+{
+    if (event->action == action_add) {
+        media_t *media;
+        char serial[80];
+        char *type;
+
+        /*
+         * Pull card information from sysfs
+         */
+        type = get_uevent_param(event, "MMC_TYPE");
+        if (strcmp(type, "SD") && strcmp(type, "MMC"))
+            return 0;
+        
+        read_sysfs_var(serial, sizeof(serial), event->path, "serial");
+        if (!(media = media_create(event->path,
+                                   get_uevent_param(event, "MMC_NAME"),
+                                   serial,
+                                   media_mmc))) {
+            LOGE("Unable to allocate new media (%m)");
+            return -1;
+        }
+        LOG_VOL("New MMC card '%s' (serial %u) added @ %s", media->name,
+                  media->serial, media->devpath);
+    } else if (event->action == action_remove) {
+        media_t *media;
+
+        if (!(media = media_lookup_by_path(event->path, false))) {
+            LOGE("Unable to lookup media '%s'", event->path);
+            return -1;
+        }
+
+        LOG_VOL("MMC card '%s' (serial %u) @ %s removed", media->name, 
+                  media->serial, media->devpath);
+        media_destroy(media);
+    } else {
+#if DEBUG_UEVENT
+        LOG_VOL("No handler implemented for action %d", event->action);
+#endif
+    }
+
+    return 0;
+}
diff --git a/vold/uevent.h b/vold/uevent.h
new file mode 100644
index 0000000..0e9e671
--- /dev/null
+++ b/vold/uevent.h
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UEVENT_MSG_H
+#define _UEVENT_MSG_H
+
+#endif
diff --git a/vold/ums.c b/vold/ums.c
new file mode 100644
index 0000000..4d0fc25
--- /dev/null
+++ b/vold/ums.c
@@ -0,0 +1,129 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include "vold.h"
+#include "ums.h"
+
+#define DEBUG_UMS 0
+
+static boolean host_connected = false;
+static boolean ums_enabled = false;
+
+int ums_bootstrap(void)
+{
+    return 0;
+}
+
+void ums_enabled_set(boolean enabled)
+{
+    ums_enabled = enabled;
+    send_msg(enabled ? VOLD_EVT_UMS_ENABLED : VOLD_EVT_UMS_DISABLED);
+}
+
+boolean ums_enabled_get()
+{
+    return ums_enabled;
+}
+
+void ums_hostconnected_set(boolean connected)
+{
+#if DEBUG_UMS
+    LOG_VOL("ums_hostconnected_set(%d):", connected);
+#endif
+    host_connected = connected;
+
+    if (!connected)
+        ums_enabled_set(false);
+    send_msg(connected ? VOLD_EVT_UMS_CONNECTED : VOLD_EVT_UMS_DISCONNECTED);
+}
+
+int ums_enable(char *dev_fspath, char *lun_syspath)
+{
+    LOG_VOL("ums_enable(%s, %s):", dev_fspath, lun_syspath);
+
+    int fd;
+    char filename[255];
+
+    sprintf(filename, "/sys/%s/file", lun_syspath);
+    if ((fd = open(filename, O_WRONLY)) < 0) {
+        LOGE("Unable to open '%s' (%s)", filename, strerror(errno));
+        return -errno;
+    }
+
+    if (write(fd, dev_fspath, strlen(dev_fspath)) < 0) {
+        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    
+    close(fd);
+    return 0;
+}
+
+int ums_disable(char *lun_syspath)
+{
+#if DEBUG_UMS
+    LOG_VOL("ums_disable(%s):", lun_syspath);
+#endif
+
+    int fd;
+    char filename[255];
+
+    sprintf(filename, "/sys/%s/file", lun_syspath);
+    if ((fd = open(filename, O_WRONLY)) < 0) {
+        LOGE("Unable to open '%s' (%s)", filename, strerror(errno));
+        return -errno;
+    }
+
+    char ch = 0;
+
+    if (write(fd, &ch, 1) < 0) {
+        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    
+    close(fd);
+    return 0;
+}
+
+boolean ums_hostconnected_get(void)
+{
+    return host_connected;
+}
+
+int ums_send_status(void)
+{
+    int rc;
+
+#if DEBUG_UMS
+    LOG_VOL("ums_send_status():");
+#endif
+
+    rc = send_msg(ums_enabled_get() ? VOLD_EVT_UMS_ENABLED :
+                                      VOLD_EVT_UMS_DISABLED);
+    if (rc < 0)
+        return rc;
+
+    rc = send_msg(ums_hostconnected_get() ? VOLD_EVT_UMS_CONNECTED :
+                                            VOLD_EVT_UMS_DISCONNECTED);
+
+    return rc;
+}
diff --git a/vold/ums.h b/vold/ums.h
new file mode 100644
index 0000000..02cdec3
--- /dev/null
+++ b/vold/ums.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UMS_H
+#define _UMS_H
+
+// these must match the corresponding strings in java/android/android/os/UsbListener.java
+#define VOLD_EVT_UMS_ENABLED              "ums_enabled"
+#define VOLD_EVT_UMS_DISABLED             "ums_disabled"
+#define VOLD_EVT_UMS_CONNECTED            "ums_connected"
+#define VOLD_EVT_UMS_DISCONNECTED         "ums_disconnected"
+
+
+int ums_send_status(void);
+int ums_enable(char *device_file, char *lun_syspath);
+int ums_disable(char *lun_syspath);
+#endif
diff --git a/vold/vold.c b/vold/vold.c
new file mode 100644
index 0000000..17331ac
--- /dev/null
+++ b/vold/vold.c
@@ -0,0 +1,234 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/cpu_info.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include <linux/netlink.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "vold.h"
+#include "volmgr.h"
+
+
+#define VOLD_SOCKET "vold"
+
+/*
+ * Globals
+ */
+
+static int ver_major = 2;
+static int ver_minor = 0;
+static pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int fw_sock = -1;
+
+int main(int argc, char **argv)
+{
+    int door_sock = -1;
+    int uevent_sock = -1;
+    struct sockaddr_nl nladdr;
+    int uevent_sz = 64 * 1024;
+
+    LOG_VOL("Android Volume Daemon version %d.%d", ver_major, ver_minor);
+
+    /*
+     * Create all the various sockets we'll need
+     */
+
+    // Socket to listen on for incomming framework connections
+    if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) {
+        LOGE("Obtaining file descriptor socket '%s' failed: %s",
+             VOLD_SOCKET, strerror(errno));
+        exit(1);
+    }
+
+    if (listen(door_sock, 4) < 0) {
+        LOGE("Unable to listen on fd '%d' for socket '%s': %s", 
+             door_sock, VOLD_SOCKET, strerror(errno));
+        exit(1);
+    }
+
+    mkdir("/dev/block/vold", 0755);
+
+    // Socket to listen on for uevent changes
+    memset(&nladdr, 0, sizeof(nladdr));
+    nladdr.nl_family = AF_NETLINK;
+    nladdr.nl_pid = getpid();
+    nladdr.nl_groups = 0xffffffff;
+
+    if ((uevent_sock = socket(PF_NETLINK,
+                             SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
+        LOGE("Unable to create uevent socket: %s", strerror(errno));
+        exit(1);
+    }
+
+    if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,
+                   sizeof(uevent_sz)) < 0) {
+        LOGE("Unable to set uevent socket options: %s", strerror(errno));
+        exit(1);
+    }
+
+    if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
+        LOGE("Unable to bind uevent socket: %s", strerror(errno));
+        exit(1);
+    }
+
+    /*
+     * Bootstrap 
+     */
+
+    // Volume Manager
+    volmgr_bootstrap();
+
+    // SD Card system
+    mmc_bootstrap();
+
+    // USB Mass Storage
+    ums_bootstrap();
+
+    // Switch
+    switch_bootstrap();
+
+    /*
+     * Main loop
+     */
+    LOG_VOL("Bootstrapping complete");
+    while(1) {
+        fd_set read_fds;
+        struct timeval to;
+        int max = 0;
+        int rc = 0;
+
+        to.tv_sec = (60 * 60);
+        to.tv_usec = 0;
+
+        FD_ZERO(&read_fds);
+        FD_SET(door_sock, &read_fds);
+        if (door_sock > max)
+            max = door_sock;
+        FD_SET(uevent_sock, &read_fds);
+        if (uevent_sock > max)
+            max = uevent_sock;
+
+        if (fw_sock != -1) {
+            FD_SET(fw_sock, &read_fds);
+            if (fw_sock > max)
+                max = fw_sock;
+        }
+
+        if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {
+            LOGE("select() failed (%s)", strerror(errno));
+            sleep(1);
+            continue;
+        }
+
+        if (!rc) {
+            continue;
+        }
+
+        if (FD_ISSET(door_sock, &read_fds)) {
+            struct sockaddr addr;
+            socklen_t alen;
+
+            alen = sizeof(addr);
+
+            if (fw_sock != -1) {
+                LOGE("Dropping duplicate framework connection");
+                int tmp = accept(door_sock, &addr, &alen);
+                close(tmp);
+                continue;
+            }
+
+            if ((fw_sock = accept(door_sock, &addr, &alen)) < 0) {
+                LOGE("Unable to accept framework connection (%s)",
+                     strerror(errno));
+            }
+            LOG_VOL("Accepted connection from framework");
+            if ((rc = volmgr_send_states()) < 0) {
+                LOGE("Unable to send volmgr status to framework (%d)", rc);
+            }
+        }
+
+        if (FD_ISSET(fw_sock, &read_fds)) {
+            if ((rc = process_framework_command(fw_sock)) < 0) {
+                if (rc == -ECONNRESET) {
+                    LOGE("Framework disconnected");
+                    close(fw_sock);
+                    fw_sock = -1;
+                } else {
+                    LOGE("Error processing framework command (%s)",
+                         strerror(errno));
+                }
+            }
+        }
+
+        if (FD_ISSET(uevent_sock, &read_fds)) {
+            if ((rc = process_uevent_message(uevent_sock)) < 0) {
+                LOGE("Error processing uevent msg (%s)", strerror(errno));
+            }
+        }
+    } // while
+
+}
+
+int send_msg(char* message)
+{
+    int result = -1;
+
+    pthread_mutex_lock(&write_mutex);
+
+    LOG_VOL("send_msg(%s):", message);
+
+    if (fw_sock >= 0)
+        result = write(fw_sock, message, strlen(message) + 1);
+
+    pthread_mutex_unlock(&write_mutex);
+
+    return result;
+}
+
+int send_msg_with_data(char *message, char *data)
+{
+    int result = -1;
+
+    char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
+    if (!buffer) {
+        LOGE("alloca failed in send_msg_with_data");
+        return -1;
+    }
+
+    strcpy(buffer, message);
+    strcat(buffer, data);
+    return send_msg(buffer);
+}
diff --git a/vold/vold.h b/vold/vold.h
new file mode 100644
index 0000000..0876bec
--- /dev/null
+++ b/vold/vold.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOLD_H__
+#define VOLD_H__
+
+#define LOG_TAG "vold"
+#include "cutils/log.h"
+
+typedef int boolean;
+enum {
+    false = 0,
+    true = 1
+};
+
+#define DEVPATH "/dev/block/"
+#define DEVPATHLENGTH 11
+
+#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
+
+// Set this for logging error messages
+#define ENABLE_LOG_ERROR
+
+// set this to log vold events
+#define ENABLE_LOG_VOL
+
+#ifdef ENABLE_LOG_ERROR
+#define LOG_ERROR(fmt, args...) \
+    { LOGE(fmt , ## args); }
+#else
+#define LOG_ERROR(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_ERROR */
+
+#ifdef ENABLE_LOG_VOL
+#define LOG_VOL(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_VOL(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_VOL */
+
+#ifdef ENABLE_LOG_SERVER
+#define LOG_SERVER(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_SERVER(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_SERVER */
+
+#ifdef ENABLE_LOG_ASEC
+#define LOG_ASEC(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_ASEC(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_ASEC */
+
+/*
+ * Prototypes
+ */
+
+int process_framework_command(int socket);
+
+int process_inotify_event(int fd);
+int inotify_bootstrap(void);
+
+int process_uevent_message(int socket);
+int simulate_uevent(char *subsystem, char *path, char *action, char **params);
+
+int mmc_bootstrap(void);
+int ums_bootstrap(void);
+
+int volmgr_bootstrap(void);
+
+int switch_bootstrap(void);
+
+void *read_file(char *filename, ssize_t *_size);
+char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer);
+char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var);
+
+void ums_hostconnected_set(boolean connected);
+boolean ums_hostconnected_get(void);
+
+int send_msg(char *msg);
+int send_msg_with_data(char *msg, char *data);
+#endif
diff --git a/vold/volmgr.c b/vold/volmgr.c
new file mode 100644
index 0000000..c3ce8d1
--- /dev/null
+++ b/vold/volmgr.c
@@ -0,0 +1,1229 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include <sys/mount.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/properties.h>
+
+#include "vold.h"
+#include "volmgr.h"
+#include "blkdev.h"
+#include "ums.h"
+#include "format.h"
+#include "devmapper.h"
+
+#include "volmgr_ext3.h"
+#include "volmgr_vfat.h"
+
+#define DEBUG_VOLMGR 0
+
+static volume_t *vol_root = NULL;
+static boolean safe_mode = true;
+
+static struct volmgr_fstable_entry fs_table[] = {
+    { "ext3", ext_identify, ext_check, ext_mount , true },
+    { "vfat", vfat_identify, vfat_check, vfat_mount , false },
+    { NULL, NULL, NULL, NULL , false}
+};
+
+struct _volume_state_event_map {
+    volume_state_t state;
+    char           *event;
+    char           *property_val;
+};
+
+static struct _volume_state_event_map volume_state_strings[] = {
+    { volstate_unknown,     "volstate_unknown:",  "unknown" },
+    { volstate_nomedia,     VOLD_EVT_NOMEDIA,     VOLD_ES_PVAL_NOMEDIA },
+    { volstate_unmounted,   VOLD_EVT_UNMOUNTED,   VOLD_ES_PVAL_UNMOUNTED },
+    { volstate_checking,    VOLD_EVT_CHECKING,    VOLD_ES_PVAL_CHECKING },
+    { volstate_mounted,     VOLD_EVT_MOUNTED,     VOLD_ES_PVAL_MOUNTED },
+    { volstate_mounted_ro,  VOLD_EVT_MOUNTED_RO,  VOLD_ES_PVAL_MOUNTED_RO },
+    { volstate_badremoval,  VOLD_EVT_BADREMOVAL,  VOLD_ES_PVAL_BADREMOVAL },
+    { volstate_damaged,     VOLD_EVT_DAMAGED,     VOLD_ES_PVAL_DAMAGED },
+    { volstate_nofs,        VOLD_EVT_NOFS,        VOLD_ES_PVAL_NOFS },
+    { volstate_ums,         VOLD_EVT_UMS,         VOLD_ES_PVAL_UMS },
+    { 0, NULL, NULL }
+};
+
+
+static int volmgr_readconfig(char *cfg_path);
+static int volmgr_config_volume(cnode *node);
+static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy);
+static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev);
+static int _volmgr_start(volume_t *vol, blkdev_t *dev);
+static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev);
+static void *volmgr_start_fs_thread(void *arg);
+static void volmgr_start_fs_thread_sighandler(int signo);
+static void volume_setstate(volume_t *vol, volume_state_t state);
+static char *conv_volstate_to_eventstr(volume_state_t state);
+static char *conv_volstate_to_propstr(volume_state_t state);
+static int volume_send_state(volume_t *vol);
+static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg);
+static int _volmgr_enable_ums(volume_t *);
+static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *arg), boolean emit_statechange);
+static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange);
+static void _cb_volume_stopped_for_eject(volume_t *v, void *arg);
+static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg);
+static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev);
+static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg);
+static void volmgr_reaper_thread_sighandler(int signo);
+static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path);
+static int volmgr_send_eject_request(volume_t *v);
+static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked);
+
+static boolean _mountpoint_mounted(char *mp)
+{
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    FILE *fp;
+    char line[1024];
+
+    if (!(fp = fopen("/proc/mounts", "r"))) {
+        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
+        return false;
+    }
+
+    while(fgets(line, sizeof(line), fp)) {
+        line[strlen(line)-1] = '\0';
+        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
+        if (!strcmp(mount_path, mp)) {
+            fclose(fp);
+            return true;
+        }
+        
+    }
+
+    fclose(fp);
+    return false;
+}
+
+/*
+ * Public functions
+ */
+
+int volmgr_set_volume_key(char *mount_point, unsigned char *key)
+{
+    volume_t *v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+ 
+    if (!v)
+        return -ENOENT;
+
+    if (v->media_type != media_devmapper) {
+        LOGE("Cannot set key on a non devmapper volume");
+        pthread_mutex_unlock(&v->lock);
+        return -EINVAL;
+    }
+
+    memcpy(v->dm->key, key, sizeof(v->dm->key));
+    pthread_mutex_unlock(&v->lock);
+    return 0;
+}
+
+int volmgr_format_volume(char *mount_point)
+{
+    int rc;
+    volume_t *v;
+
+    LOG_VOL("volmgr_format_volume(%s):", mount_point);
+
+    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+
+    if (!v)
+        return -ENOENT;
+
+    if (v->state == volstate_mounted ||
+        v->state == volstate_mounted_ro ||
+        v->state == volstate_ums ||
+        v->state == volstate_checking) {
+            LOGE("Can't format '%s', currently in state %d", mount_point, v->state);
+            pthread_mutex_unlock(&v->lock);
+            return -EBUSY;
+        } else if (v->state == volstate_nomedia &&
+                   v->media_type != media_devmapper) {
+            LOGE("Can't format '%s', (no media)", mount_point);
+            pthread_mutex_unlock(&v->lock);
+            return -ENOMEDIUM;
+        }
+
+    // XXX:Reject if the underlying source media is not present
+
+    if (v->media_type == media_devmapper) {
+        if ((rc = devmapper_genesis(v->dm)) < 0) {
+            LOGE("devmapper genesis failed for %s (%d)", mount_point, rc);
+            pthread_mutex_unlock(&v->lock);
+            return rc;
+        }
+    } else {
+        if ((rc = initialize_mbr(v->dev->disk)) < 0) {
+            LOGE("MBR init failed for %s (%d)", mount_point, rc);
+            pthread_mutex_unlock(&v->lock);
+            return rc;
+        }
+    }
+
+    volume_setstate(v, volstate_formatting);
+    pthread_mutex_unlock(&v->lock);
+    return rc;
+}
+
+int volmgr_bootstrap(void)
+{
+    int rc;
+
+    if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) {
+        LOGE("Unable to process config");
+        return rc;
+    }
+
+    /*
+     * Check to see if any of our volumes is mounted
+     */
+    volume_t *v = vol_root;
+    while (v) {
+        if (_mountpoint_mounted(v->mount_point)) {
+            LOG_VOL("Volume '%s' already mounted at startup", v->mount_point);
+            v->state = volstate_mounted;
+        }
+        v = v->next;
+    }
+
+    return 0;
+}
+
+int volmgr_safe_mode(boolean enable)
+{
+    if (enable == safe_mode)
+        return 0;
+
+    safe_mode = enable;
+
+    volume_t *v = vol_root;
+    int rc;
+
+    while (v) {
+        pthread_mutex_lock(&v->lock);
+        if (v->state == volstate_mounted && v->fs) {
+            rc = v->fs->mount_fn(v->dev, v, safe_mode);
+            if (!rc) {
+                LOG_VOL("Safe mode %s on %s", (enable ? "enabled" : "disabled"), v->mount_point);
+            } else {
+                LOGE("Failed to %s safe-mode on %s (%s)",
+                     (enable ? "enable" : "disable" ), v->mount_point, strerror(-rc));
+            }
+        }
+
+        pthread_mutex_unlock(&v->lock);
+        v = v->next;
+    }
+        
+    return 0;
+}
+
+int volmgr_send_states(void)
+{
+    volume_t *vol_scan = vol_root;
+    int rc;
+
+    while (vol_scan) {
+        pthread_mutex_lock(&vol_scan->lock);
+        if ((rc = volume_send_state(vol_scan)) < 0) {
+            LOGE("Error sending state to framework (%d)", rc);
+        }
+        pthread_mutex_unlock(&vol_scan->lock);
+        vol_scan = vol_scan->next;
+        break; // XXX:
+    }
+
+    return 0;
+}
+
+/*
+ * Called when a block device is ready to be
+ * evaluated by the volume manager.
+ */
+int volmgr_consider_disk(blkdev_t *dev)
+{
+    volume_t *vol;
+
+    if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true)))
+        return 0;
+
+    pthread_mutex_lock(&vol->lock);
+
+    if (vol->state == volstate_mounted) {
+        LOGE("Volume %s already mounted (did we just crash?)", vol->mount_point);
+        pthread_mutex_unlock(&vol->lock);
+        return 0;
+    }
+
+    int rc =  _volmgr_consider_disk_and_vol(vol, dev);
+    pthread_mutex_unlock(&vol->lock);
+    return rc;
+}
+
+int volmgr_start_volume_by_mountpoint(char *mount_point)
+{ 
+    volume_t *v;
+
+    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+    if (!v)
+        return -ENOENT;
+
+    if (v->media_type == media_devmapper) {
+        if (devmapper_start(v->dm) < 0)  {
+            LOGE("volmgr failed to start devmapper volume '%s'",
+                 v->mount_point);
+        }
+    } else if (v->media_type == media_mmc) {
+        if (!v->dev) {
+            LOGE("Cannot start volume '%s' (volume is not bound)", mount_point);
+            pthread_mutex_unlock(&v->lock);
+            return -ENOENT;
+        }
+
+        if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) {
+            LOGE("volmgr failed to start volume '%s'", v->mount_point);
+        }
+    }
+
+    pthread_mutex_unlock(&v->lock);
+    return 0;
+}
+
+static void _cb_volstopped_for_devmapper_teardown(volume_t *v, void *arg)
+{
+    devmapper_stop(v->dm);
+    volume_setstate(v, volstate_nomedia);
+    pthread_mutex_unlock(&v->lock);
+}
+
+int volmgr_stop_volume_by_mountpoint(char *mount_point)
+{
+    int rc;
+    volume_t *v;
+
+    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+    if (!v)
+        return -ENOENT;
+
+    if (v->state == volstate_mounted)
+        volmgr_send_eject_request(v);
+
+    if (v->media_type == media_devmapper)
+        rc = volmgr_shutdown_volume(v, _cb_volstopped_for_devmapper_teardown, false);
+    else
+        rc = volmgr_shutdown_volume(v, NULL, true);
+
+    /*
+     * If shutdown returns -EINPROGRESS,
+     * do *not* release the lock as
+     * it is now owned by the reaper thread
+     */
+    if (rc != -EINPROGRESS) {
+        if (rc)
+            LOGE("unable to shutdown volume '%s'", v->mount_point);
+        pthread_mutex_unlock(&v->lock);
+    }
+    return 0;
+}
+
+int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *))
+{
+    LOG_VOL("Volmgr notified of %d:%d eject", dev->major, dev->minor);
+
+    volume_t *v;
+    int rc;
+
+    // XXX: Partitioning support is going to need us to stop *all*
+    // devices in this volume
+    if (!(v = volmgr_lookup_volume_by_dev(dev))) {
+        if (cb)
+            cb(dev);
+        return 0;
+    }
+    
+    pthread_mutex_lock(&v->lock);
+
+    volume_state_t old_state = v->state;
+
+    if (v->state == volstate_mounted ||
+        v->state == volstate_ums ||
+        v->state == volstate_checking) {
+
+        volume_setstate(v, volstate_badremoval);
+
+        /*
+         * Stop any devmapper volumes which
+         * are using us as a source
+         * XXX: We may need to enforce stricter
+         * order here
+         */
+        volume_t *dmvol = vol_root;
+        while (dmvol) {
+            if ((dmvol->media_type == media_devmapper) &&
+                (dmvol->dm->src_type == dmsrc_loopback) &&
+                (!strncmp(dmvol->dm->type_data.loop.loop_src, 
+                          v->mount_point, strlen(v->mount_point)))) {
+
+                pthread_mutex_lock(&dmvol->lock);
+                if (dmvol->state != volstate_nomedia) {
+                    rc = volmgr_shutdown_volume(dmvol, _cb_volstopped_for_devmapper_teardown, false);
+                    if (rc != -EINPROGRESS) {
+                        if (rc)
+                            LOGE("unable to shutdown volume '%s'", v->mount_point);
+                        pthread_mutex_unlock(&dmvol->lock);
+                    }
+                } else 
+                    pthread_mutex_unlock(&dmvol->lock);
+            }
+            dmvol = dmvol->next;
+        }
+
+    } else if (v->state == volstate_formatting) {
+        /*
+         * The device is being ejected due to
+         * kernel disk revalidation.
+         */
+        LOG_VOL("Volmgr ignoring eject of %d:%d (volume formatting)",
+                dev->major, dev->minor);
+        if (cb)
+            cb(dev);
+        pthread_mutex_unlock(&v->lock);
+        return 0;
+    } else
+        volume_setstate(v, volstate_nomedia);
+    
+    if (old_state == volstate_ums) {
+        ums_disable(v->ums_path);
+        pthread_mutex_unlock(&v->lock);
+    } else {
+        int rc = volmgr_stop_volume(v, _cb_volume_stopped_for_eject, cb, false);
+        if (rc != -EINPROGRESS) {
+            if (rc)
+                LOGE("unable to shutdown volume '%s'", v->mount_point);
+            pthread_mutex_unlock(&v->lock);
+        }
+    }
+    return 0; 
+}
+
+static void _cb_volume_stopped_for_eject(volume_t *v, void *arg)
+{
+    void (* eject_cb) (blkdev_t *) = arg;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("Volume %s has been stopped for eject", v->mount_point);
+#endif
+
+    if (eject_cb)
+        eject_cb(v->dev);
+    v->dev = NULL; // Clear dev because its being ejected
+}
+
+/*
+ * Instructs the volume manager to enable or disable USB mass storage
+ * on any volumes configured to use it.
+ */
+int volmgr_enable_ums(boolean enable)
+{
+    volume_t *v = vol_root;
+
+    while(v) {
+        if (v->ums_path) {
+            int rc;
+
+            if (enable) {
+                pthread_mutex_lock(&v->lock);
+                if (v->state == volstate_mounted)
+                    volmgr_send_eject_request(v);
+                else if (v->state == volstate_ums) {
+                    pthread_mutex_unlock(&v->lock);
+                    goto next_vol;
+                }
+
+                // Stop the volume, and enable UMS in the callback
+                rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable, false);
+                if (rc != -EINPROGRESS) {
+                    if (rc)
+                        LOGE("unable to shutdown volume '%s'", v->mount_point);
+                    pthread_mutex_unlock(&v->lock);
+                }
+            } else {
+                // Disable UMS
+                pthread_mutex_lock(&v->lock);
+                if (v->state != volstate_ums) {
+                    pthread_mutex_unlock(&v->lock);
+                    goto next_vol;
+                }
+
+                if ((rc = ums_disable(v->ums_path)) < 0) {
+                    LOGE("unable to disable ums on '%s'", v->mount_point);
+                    pthread_mutex_unlock(&v->lock);
+                    continue;
+                }
+
+                LOG_VOL("Kick-starting volume %d:%d after UMS disable",
+                        v->dev->disk->major, v->dev->disk->minor);
+                // Start volume
+                if ((rc = _volmgr_consider_disk_and_vol(v, v->dev->disk)) < 0) {
+                    LOGE("volmgr failed to consider disk %d:%d",
+                         v->dev->disk->major, v->dev->disk->minor);
+                }
+                pthread_mutex_unlock(&v->lock);
+            }
+        }
+ next_vol:
+        v = v->next;
+    }
+    return 0;
+}
+
+/*
+ * Static functions
+ */
+
+static int volmgr_send_eject_request(volume_t *v)
+{
+    return send_msg_with_data(VOLD_EVT_EJECTING, v->mount_point);
+}
+
+// vol->lock must be held!
+static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev)
+{
+    int rc = 0;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("volmgr_consider_disk_and_vol(%s, %d:%d):", vol->mount_point,
+            dev->major, dev->minor); 
+#endif
+
+    if (vol->state == volstate_unknown ||
+        vol->state == volstate_mounted ||
+        vol->state == volstate_mounted_ro ||
+        vol->state == volstate_damaged) {
+        LOGE("Cannot consider volume '%s' because it is in state '%d", 
+             vol->mount_point, vol->state);
+        return -EADDRINUSE;
+    }
+
+    if (vol->state == volstate_formatting) {
+        LOG_VOL("Evaluating dev '%s' for formattable filesystems for '%s'",
+                dev->devpath, vol->mount_point);
+        /*
+         * Since we only support creating 1 partition (right now),
+         * we can just lookup the target by devno
+         */
+        blkdev_t *part = blkdev_lookup_by_devno(dev->major, 1);
+        if (!part) {
+            part = blkdev_lookup_by_devno(dev->major, 0);
+            if (!part) {
+                LOGE("Unable to find device to format");
+                return -ENODEV;
+            }
+        }
+
+        if ((rc = format_partition(part,
+                                   vol->media_type == media_devmapper ?
+                                   FORMAT_TYPE_EXT2 : FORMAT_TYPE_FAT32)) < 0) {
+            LOGE("format failed (%d)", rc);
+            return rc;
+        }
+        
+    }
+
+    LOG_VOL("Evaluating dev '%s' for mountable filesystems for '%s'",
+            dev->devpath, vol->mount_point);
+
+    if (dev->nr_parts == 0) {
+        rc = _volmgr_start(vol, dev);
+#if DEBUG_VOLMGR
+        LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", vol->mount_point,
+                dev->major, dev->minor, rc);
+#endif
+    } else {
+        /*
+         * Device has multiple partitions
+         * This is where interesting partition policies could be implemented.
+         * For now just try them in sequence until one succeeds
+         */
+   
+        rc = -ENODEV;
+        int i;
+        for (i = 0; i < dev->nr_parts; i++) {
+            blkdev_t *part = blkdev_lookup_by_devno(dev->major, (i+1));
+            if (!part) {
+                LOGE("Error - unable to lookup partition for blkdev %d:%d", dev->major, (i+1));
+                continue;
+            }
+            rc = _volmgr_start(vol, part);
+#if DEBUG_VOLMGR
+            LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d",
+                    vol->mount_point, part->major, part->minor, rc);
+#endif
+            if (!rc) 
+                break;
+        }
+
+        if (rc == -ENODEV) {
+            // Assert to make sure each partition had a backing blkdev
+            LOGE("Internal consistency error");
+            return 0;
+        }
+    }
+
+    if (rc == -ENODATA) {
+        LOGE("Device %d:%d contains no usable filesystems",
+             dev->major, dev->minor);
+        rc = 0;
+    }
+
+    return rc;
+}
+
+static void volmgr_reaper_thread_sighandler(int signo)
+{
+    LOGE("Volume reaper thread got signal %d", signo);
+}
+
+static void __reaper_cleanup(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+    if (vol->worker_args.reaper_args.cb)
+        vol->worker_args.reaper_args.cb(vol, vol->worker_args.reaper_args.cb_arg);
+
+    vol->worker_running = false;
+
+    // Wake up anyone that was waiting on this thread
+    pthread_mutex_unlock(&vol->worker_sem);
+
+    // Unlock the volume
+    pthread_mutex_unlock(&vol->lock);
+}
+
+static void *volmgr_reaper_thread(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+    pthread_cleanup_push(__reaper_cleanup, arg);
+
+    vol->worker_running = true;
+    vol->worker_pid = getpid();
+
+    struct sigaction actions;
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = volmgr_reaper_thread_sighandler;
+    sigaction(SIGUSR1, &actions, NULL);
+
+    LOG_VOL("Reaper here - working on %s", vol->mount_point);
+
+    boolean send_sig_kill = false;
+    int i, rc;
+
+    for (i = 0; i < 10; i++) {
+        errno = 0;
+        rc = umount(vol->mount_point);
+        LOG_VOL("volmngr reaper umount(%s) attempt %d (%s)",
+                vol->mount_point, i + 1, strerror(errno));
+        if (!rc)
+            break;
+        if (rc && (errno == EINVAL || errno == ENOENT)) {
+            rc = 0;
+            break;
+        }
+        sleep(1);
+        if (i >= 4) {
+            KillProcessesWithOpenFiles(vol->mount_point, send_sig_kill, NULL, 0);
+            if (!send_sig_kill)
+                send_sig_kill = true;
+        }
+    }
+
+    if (!rc) {
+        LOG_VOL("Reaper sucessfully unmounted %s", vol->mount_point);
+        vol->fs = NULL;
+        volume_setstate(vol, volstate_unmounted);
+    } else {
+        LOGE("Unable to unmount!! (%d)", rc);
+    }
+
+ out:
+    pthread_cleanup_pop(1);
+    pthread_exit(NULL);
+    return NULL;
+}
+
+// vol->lock must be held!
+static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg)
+{
+
+    if (vol->worker_running) {
+        LOGE("Worker thread is currently running.. waiting..");
+        pthread_mutex_lock(&vol->worker_sem);
+        LOG_VOL("Worker thread now available");
+    }
+
+    vol->worker_args.reaper_args.cb = cb;
+    vol->worker_args.reaper_args.cb_arg = arg;
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&vol->worker_thread, &attr, volmgr_reaper_thread, vol);
+}
+
+static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange)
+{
+    int i, rc;
+
+    if (v->state == volstate_mounted || v->state == volstate_badremoval) {
+        // Try to unmount right away (5 retries)
+        for (i = 0; i < 5; i++) {
+            rc = umount(v->mount_point);
+            if (!rc)
+                break;
+
+            if (rc && (errno == EINVAL || errno == ENOENT)) {
+                rc = 0;
+                break;
+            }
+
+            LOG_VOL("volmngr quick stop umount(%s) attempt %d (%s)",
+                    v->mount_point, i + 1, strerror(errno));
+
+            if (i == 0)
+                usleep(1000 * 250); // First failure, sleep for 250 ms 
+            else
+                sched_yield();
+        }
+
+        if (!rc) {
+            LOG_VOL("volmgr_stop_volume(%s): Volume unmounted sucessfully",
+                    v->mount_point);
+            if (emit_statechange)
+                volume_setstate(v, volstate_unmounted);
+            v->fs = NULL;
+            goto out_cb_immed;
+        }
+
+        /*
+         * Since the volume is still in use, dispatch the stopping to
+         * a thread
+         */
+        LOG_VOL("Volume %s is busy (%d) - uncaging the reaper", v->mount_point, rc);
+        volmgr_uncage_reaper(v, cb, arg);
+        return -EINPROGRESS;
+    } else if (v->state == volstate_checking) {
+        volume_setstate(v, volstate_unmounted);
+        if (v->worker_running) {
+            LOG_VOL("Cancelling worker thread");
+            pthread_kill(v->worker_thread, SIGUSR1);
+        } else
+            LOGE("Strange... we were in checking state but worker thread wasn't running..");
+        goto out_cb_immed;
+    }
+
+ out_cb_immed:
+    if (cb)
+        cb(v, arg);
+    return 0;
+}
+
+
+/*
+ * Gracefully stop a volume
+ * v->lock must be held!
+ * if we return -EINPROGRESS, do NOT release the lock as the reaper
+ * is using the volume
+ */
+static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *), boolean emit_statechange)
+{
+    return volmgr_stop_volume(v, cb, NULL, emit_statechange);
+}
+
+static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg)
+{
+    void (* shutdown_cb) (volume_t *) = arg;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("Volume %s has been stopped for shutdown", v->mount_point);
+#endif
+    shutdown_cb(v);
+}
+
+
+/*
+ * Called when a volume is sucessfully unmounted for UMS enable
+ */
+static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
+{
+    int rc;
+    char *devdir_path;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("_cb_volstopped_for_ums_enable(%s):", v->mount_point);
+#endif
+    devdir_path = blkdev_get_devpath(v->dev->disk);
+
+    if ((rc = ums_enable(devdir_path, v->ums_path)) < 0) {
+        free(devdir_path);
+        LOGE("Error enabling ums (%d)", rc);
+        return;
+    }
+    free(devdir_path);
+    volume_setstate(v, volstate_ums);
+    pthread_mutex_unlock(&v->lock);
+}
+
+static int volmgr_readconfig(char *cfg_path)
+{
+    cnode *root = config_node("", "");
+    cnode *node;
+
+    config_load_file(root, cfg_path);
+    node = root->first_child;
+
+    while (node) {
+        if (!strncmp(node->name, "volume_", 7))
+            volmgr_config_volume(node);
+        else
+            LOGE("Skipping unknown configuration node '%s'", node->name);
+        node = node->next;
+    }
+    return 0;
+}
+
+static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path)
+{
+    int i;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("volmgr_add_mediapath_to_volume(%p, %s):", v, media_path);
+#endif
+    for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
+        if (!v->media_paths[i]) {
+            v->media_paths[i] = strdup(media_path);
+            return;
+        }
+    }
+    LOGE("Unable to add media path '%s' to volume (out of media slots)", media_path);
+}
+
+static int volmgr_config_volume(cnode *node)
+{
+    volume_t *new;
+    int rc = 0, i;
+
+    char *dm_src, *dm_src_type, *dm_tgt, *dm_param, *dm_tgtfs;
+    uint32_t dm_size_mb = 0;
+
+    dm_src = dm_src_type = dm_tgt = dm_param = dm_tgtfs = NULL;
+#if DEBUG_VOLMGR
+    LOG_VOL("volmgr_configure_volume(%s):", node->name);
+#endif
+    if (!(new = malloc(sizeof(volume_t))))
+        return -ENOMEM;
+    memset(new, 0, sizeof(volume_t));
+
+    new->state = volstate_nomedia;
+    pthread_mutex_init(&new->lock, NULL);
+    pthread_mutex_init(&new->worker_sem, NULL);
+
+    cnode *child = node->first_child;
+
+    while (child) {
+        if (!strcmp(child->name, "media_path"))
+            volmgr_add_mediapath_to_volume(new, child->value);
+        else if (!strcmp(child->name, "emu_media_path"))
+            volmgr_add_mediapath_to_volume(new, child->value);
+        else if (!strcmp(child->name, "media_type")) {
+            if (!strcmp(child->value, "mmc"))
+                new->media_type = media_mmc;
+            else if (!strcmp(child->value, "devmapper"))
+                new->media_type = media_devmapper;
+            else {
+                LOGE("Invalid media type '%s'", child->value);
+                rc = -EINVAL;
+                goto out_free;
+            }
+        } else if (!strcmp(child->name, "mount_point"))
+            new->mount_point = strdup(child->value);
+        else if (!strcmp(child->name, "ums_path"))
+            new->ums_path = strdup(child->value);
+        else if (!strcmp(child->name, "dm_src")) 
+            dm_src = strdup(child->value);
+        else if (!strcmp(child->name, "dm_src_type")) 
+            dm_src_type = strdup(child->value);
+        else if (!strcmp(child->name, "dm_src_size_mb")) 
+            dm_size_mb = atoi(child->value);
+        else if (!strcmp(child->name, "dm_target")) 
+            dm_tgt = strdup(child->value);
+        else if (!strcmp(child->name, "dm_target_params")) 
+            dm_param = strdup(child->value);
+        else if (!strcmp(child->name, "dm_target_fs")) 
+            dm_tgtfs = strdup(child->value);
+        else
+            LOGE("Ignoring unknown config entry '%s'", child->name);
+        child = child->next;
+    }
+
+    if (new->media_type == media_mmc) {
+        if (!new->media_paths[0] || !new->mount_point || new->media_type == media_unknown) {
+            LOGE("Required configuration parameter missing for mmc volume");
+            rc = -EINVAL;
+            goto out_free;
+        }
+    } else if (new->media_type == media_devmapper) {
+        if (!dm_src || !dm_src_type || !dm_tgt ||
+            !dm_param || !dm_tgtfs || !dm_size_mb) {
+            LOGE("Required configuration parameter missing for devmapper volume");
+            rc = -EINVAL;
+            goto out_free;
+        }
+
+        char dm_mediapath[255];
+        if (!(new->dm = devmapper_init(dm_src, dm_src_type, dm_size_mb,
+                                       dm_tgt, dm_param, dm_tgtfs, dm_mediapath))) {
+            LOGE("Unable to initialize devmapping");
+            goto out_free;
+        }
+        LOG_VOL("media path for devmapper volume = '%s'", dm_mediapath);
+        volmgr_add_mediapath_to_volume(new, dm_mediapath);
+    }
+
+    if (!vol_root)
+        vol_root = new;
+    else {
+        volume_t *scan = vol_root;
+        while (scan->next)
+            scan = scan->next;
+        scan->next = new;
+    }
+
+    if (dm_src)
+        free(dm_src);
+    if (dm_src_type)
+        free(dm_src_type);
+    if (dm_tgt)
+        free(dm_tgt);
+    if (dm_param)
+        free(dm_param);
+    if (dm_tgtfs)
+        free(dm_tgtfs);
+
+    return rc;
+
+ out_free:
+
+    if (dm_src)
+        free(dm_src);
+    if (dm_src_type)
+        free(dm_src_type);
+    if (dm_tgt)
+        free(dm_tgt);
+    if (dm_param)
+        free(dm_param);
+    if (dm_tgtfs)
+        free(dm_tgtfs);
+
+
+    for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
+        if (new->media_paths[i])
+            free(new->media_paths[i]);
+    }
+    if (new->mount_point)
+        free(new->mount_point);
+    if (new->ums_path)
+        free(new->ums_path);
+    return rc;
+}
+
+static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev)
+{
+    volume_t *scan = vol_root;
+    while(scan) {
+        if (scan->dev == dev)
+            return scan;
+        scan = scan->next;
+    }
+    return NULL;
+}
+
+static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked)
+{
+    volume_t *v = vol_root;
+
+    while(v) {
+        pthread_mutex_lock(&v->lock);
+        if (!strcmp(v->mount_point, mount_point)) {
+            if (!leave_locked)
+                pthread_mutex_unlock(&v->lock);
+            return v;
+        }
+        pthread_mutex_unlock(&v->lock);
+        v = v->next;
+    }
+    return NULL;
+}
+
+static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy)
+{
+    volume_t *scan = vol_root;
+    int i;
+
+    while (scan) {
+
+        for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
+            if (!scan->media_paths[i])
+                continue;
+
+            if (fuzzy && !strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i])))
+                return scan;
+            else if (!fuzzy && !strcmp(media_path, scan->media_paths[i]))
+                return scan;
+        }
+
+        scan = scan->next;
+    }
+    return NULL;
+}
+
+/*
+ * Attempt to bring a volume online
+ * Returns: 0 on success, errno on failure, with the following exceptions:
+ *     - ENODATA - Unsupported filesystem type / blank
+ * vol->lock MUST be held!
+ */
+static int _volmgr_start(volume_t *vol, blkdev_t *dev)
+{
+    struct volmgr_fstable_entry *fs;
+    int rc = ENODATA;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("_volmgr_start(%s, %d:%d):", vol->mount_point,
+            dev->major, dev->minor);
+#endif
+
+    if (vol->state == volstate_mounted) {
+        LOGE("Unable to start volume '%s' (already mounted)", vol->mount_point);
+        return -EBUSY;
+    }
+
+    for (fs = fs_table; fs->name; fs++) {
+        if (!fs->identify_fn(dev))
+            break;
+    }
+
+    if (!fs) {
+        LOGE("No supported filesystems on %d:%d", dev->major, dev->minor);
+        volume_setstate(vol, volstate_nofs);
+        return -ENODATA;
+    }
+
+    return volmgr_start_fs(fs, vol, dev);
+}
+
+// vol->lock MUST be held!
+static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev)
+{
+    /*
+     * Spawn a thread to do the actual checking / mounting in
+     */
+
+    if (vol->worker_running) {
+        LOGE("Worker thread is currently running.. waiting..");
+        pthread_mutex_lock(&vol->worker_sem);
+        LOG_VOL("Worker thread now available");
+    }
+
+    vol->dev = dev; 
+
+    vol->worker_args.start_args.fs = fs;
+    vol->worker_args.start_args.dev = dev;
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol);
+
+    return 0;
+}
+
+static void __start_fs_thread_lock_cleanup(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("__start_fs_thread_lock_cleanup(%s):", vol->mount_point);
+#endif
+
+    vol->worker_running = false;
+
+    // Wake up anyone that was waiting on this thread
+    pthread_mutex_unlock(&vol->worker_sem);
+
+    // Unlock the volume
+    pthread_mutex_unlock(&vol->lock);
+}
+
+static void *volmgr_start_fs_thread(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+    pthread_cleanup_push(__start_fs_thread_lock_cleanup, arg);
+    pthread_mutex_lock(&vol->lock);
+
+    vol->worker_running = true;
+    vol->worker_pid = getpid();
+
+    struct sigaction actions;
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = volmgr_start_fs_thread_sighandler;
+    sigaction(SIGUSR1, &actions, NULL);
+
+    struct volmgr_fstable_entry *fs = vol->worker_args.start_args.fs;
+    blkdev_t                    *dev = vol->worker_args.start_args.dev;
+    int                          rc;
+  
+#if DEBUG_VOLMGR
+    LOG_VOL("Worker thread pid %d starting %s fs %d:%d on %s", getpid(),
+             fs->name, dev->major, dev->minor, vol->mount_point);
+#endif
+
+    if (fs->check_fn) {
+#if DEBUG_VOLMGR
+        LOG_VOL("Starting %s filesystem check on %d:%d", fs->name,
+                dev->major, dev->minor);
+#endif
+        volume_setstate(vol, volstate_checking);
+        pthread_mutex_unlock(&vol->lock);
+        rc = fs->check_fn(dev);
+        pthread_mutex_lock(&vol->lock);
+        if (vol->state != volstate_checking) {
+            LOG_VOL("filesystem check aborted");
+            goto out;
+        }
+        
+        if (rc < 0) {
+            LOGE("%s filesystem check failed on %d:%d (%s)", fs->name,
+                    dev->major, dev->minor, strerror(-rc));
+            if (rc == -ENODATA) {
+               volume_setstate(vol, volstate_nofs);
+               goto out;
+            }
+            goto out_unmountable;
+        }
+#if DEBUG_VOLMGR
+        LOG_VOL("%s filesystem check of %d:%d OK", fs->name,
+                dev->major, dev->minor);
+#endif
+    }
+
+    rc = fs->mount_fn(dev, vol, safe_mode);
+    if (!rc) {
+        LOG_VOL("Sucessfully mounted %s filesystem %d:%d on %s (safe-mode %s)",
+                fs->name, dev->major, dev->minor, vol->mount_point,
+                (safe_mode ? "on" : "off"));
+        vol->fs = fs;
+        volume_setstate(vol, volstate_mounted);
+        goto out;
+    }
+
+    LOGE("%s filesystem mount of %d:%d failed (%d)", fs->name, dev->major,
+         dev->minor, rc);
+
+ out_unmountable:
+    volume_setstate(vol, volstate_damaged);
+ out:
+    pthread_cleanup_pop(1);
+    pthread_exit(NULL);
+    return NULL;
+}
+
+static void volmgr_start_fs_thread_sighandler(int signo)
+{
+    LOGE("Volume startup thread got signal %d", signo);
+}
+
+static void volume_setstate(volume_t *vol, volume_state_t state)
+{
+    if (state == vol->state)
+        return;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("Volume %s state change from %d -> %d", vol->mount_point, vol->state, state);
+#endif
+    
+    vol->state = state;
+    
+    char *prop_val = conv_volstate_to_propstr(vol->state);
+
+    if (prop_val) {
+        property_set(PROP_EXTERNAL_STORAGE_STATE, prop_val);
+        volume_send_state(vol);
+    }
+}
+
+static int volume_send_state(volume_t *vol)
+{
+    char *event = conv_volstate_to_eventstr(vol->state);
+
+    return send_msg_with_data(event, vol->mount_point);
+}
+
+static char *conv_volstate_to_eventstr(volume_state_t state)
+{
+    int i;
+
+    for (i = 0; volume_state_strings[i].event != NULL; i++) {
+        if (volume_state_strings[i].state == state)
+            break;
+    }
+
+    return volume_state_strings[i].event;
+}
+
+static char *conv_volstate_to_propstr(volume_state_t state)
+{
+    int i;
+
+    for (i = 0; volume_state_strings[i].event != NULL; i++) {
+        if (volume_state_strings[i].state == state)
+            break;
+    }
+
+    return volume_state_strings[i].property_val;
+}
+
diff --git a/vold/volmgr.h b/vold/volmgr.h
new file mode 100644
index 0000000..2c7ec50
--- /dev/null
+++ b/vold/volmgr.h
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_H
+#define _VOLMGR_H
+
+#include <pthread.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "media.h"
+#include "devmapper.h"
+
+#define PROP_EXTERNAL_STORAGE_STATE "EXTERNAL_STORAGE_STATE"
+
+// these must match the corresponding states in the MediaState enum.
+// A path to the volume mount point follows the colon
+typedef enum volume_state {
+    volstate_unknown,
+
+    volstate_nomedia,
+#define VOLD_EVT_NOMEDIA       "volume_nomedia:"
+#define VOLD_ES_PVAL_NOMEDIA   "removed"
+
+    volstate_unmounted,
+#define VOLD_EVT_UNMOUNTED     "volume_unmounted:"
+#define VOLD_ES_PVAL_UNMOUNTED "unmounted"
+
+    volstate_checking,
+#define VOLD_EVT_CHECKING      "volume_checking:"
+#define VOLD_ES_PVAL_CHECKING  "checking"
+
+    volstate_mounted,
+#define VOLD_EVT_MOUNTED       "volume_mounted:"
+#define VOLD_ES_PVAL_MOUNTED   "mounted"
+
+    volstate_mounted_ro,
+#define VOLD_EVT_MOUNTED_RO     "volume_mounted_ro:"
+#define VOLD_ES_PVAL_MOUNTED_RO "mounted_ro"
+
+    volstate_badremoval,
+#define VOLD_EVT_BADREMOVAL     "volume_badremoval:"
+#define VOLD_ES_PVAL_BADREMOVAL "bad_removal"
+
+    volstate_damaged,
+#define VOLD_EVT_DAMAGED         "volume_damaged:"
+#define VOLD_ES_PVAL_DAMAGED     "unmountable"
+
+    volstate_nofs,
+#define VOLD_EVT_NOFS            "volume_nofs:"
+#define VOLD_ES_PVAL_NOFS        "nofs"
+
+    volstate_ums,
+#define VOLD_EVT_UMS             "volume_ums:"
+#define VOLD_ES_PVAL_UMS         "shared"
+
+    volstate_ejecting,
+#define VOLD_EVT_EJECTING        "volume_ejecting:"
+#define VOLD_ES_PVAL_EJECTING    "ejecting"
+
+    volstate_formatting,
+} volume_state_t;
+
+struct volume;
+
+struct volmgr_fstable_entry {
+    char *name;
+    int     (*identify_fn) (blkdev_t *dev);
+    int     (*check_fn) (blkdev_t *dev);
+    int     (*mount_fn) (blkdev_t *dev, struct volume *vol, boolean safe_mode);
+    boolean case_sensitive_paths;
+};
+
+struct volmgr_start_args {
+    struct volmgr_fstable_entry *fs;
+    blkdev_t                    *dev;
+};
+
+struct volmgr_reaper_args {
+    void (*cb) (struct volume *, void *);
+    void *cb_arg;
+};
+
+#define VOLMGR_MAX_MEDIAPATHS_PER_VOLUME 8
+
+typedef struct volume {
+    char            *media_paths[VOLMGR_MAX_MEDIAPATHS_PER_VOLUME];
+
+    media_type_t      media_type;
+    char              *mount_point;
+    char              *ums_path;
+    struct devmapping *dm;
+
+    pthread_mutex_t          lock;
+    volume_state_t           state;
+    blkdev_t                 *dev;
+    pid_t                    worker_pid;
+    pthread_t                worker_thread;
+    union {
+        struct volmgr_start_args  start_args;
+        struct volmgr_reaper_args reaper_args;
+    } worker_args;
+    boolean                  worker_running;
+    pthread_mutex_t          worker_sem;
+
+    struct volmgr_fstable_entry *fs;
+
+    struct volume            *next;
+} volume_t;
+
+int volmgr_consider_disk(blkdev_t *dev);
+int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *));
+int volmgr_send_states(void);
+int volmgr_enable_ums(boolean enable);
+int volmgr_stop_volume_by_mountpoint(char *mount_point);
+int volmgr_start_volume_by_mountpoint(char *mount_point);
+int volmgr_safe_mode(boolean enable);
+int volmgr_format_volume(char *mount_point);
+int volmgr_set_volume_key(char *mount_point, unsigned char *key);
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded);
+#endif
diff --git a/vold/volmgr_ext3.c b/vold/volmgr_ext3.c
new file mode 100644
index 0000000..680be21
--- /dev/null
+++ b/vold/volmgr_ext3.c
@@ -0,0 +1,184 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <linux/ext2_fs.h>
+#include <linux/ext3_fs.h>
+
+#include "vold.h"
+#include "volmgr.h"
+#include "volmgr_ext3.h"
+#include "logwrapper.h"
+
+
+#define EXT_DEBUG 0
+
+static char E2FSCK_PATH[] = "/system/bin/e2fsck";
+
+int ext_identify(blkdev_t *dev)
+{
+    int rc = -1;
+    int fd;
+    struct ext3_super_block sb;
+    char *devpath;
+
+#if EXT_DEBUG
+    LOG_VOL("ext_identify(%d:%d):", dev-major, dev->minor);
+#endif
+
+    devpath = blkdev_get_devpath(dev);
+
+    if ((fd = open(devpath, O_RDWR)) < 0) {
+        LOGE("Unable to open device '%s' (%s)", devpath,
+             strerror(errno));
+        free(devpath);
+        return -errno;
+    }
+
+    if (lseek(fd, 1024, SEEK_SET) < 0) {
+        LOGE("Unable to lseek to get superblock (%s)", strerror(errno));
+        rc =  -errno;
+        goto out;
+    }
+
+    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
+        LOGE("Unable to read superblock (%s)", strerror(errno));
+        rc =  -errno;
+        goto out;
+    }
+
+    if (sb.s_magic == EXT2_SUPER_MAGIC ||
+        sb.s_magic == EXT3_SUPER_MAGIC)
+        rc = 0;
+    else
+        rc = -ENODATA;
+
+ out:
+#if EXT_DEBUG
+    LOG_VOL("ext_identify(%s): rc = %d", devpath, rc);
+#endif
+    free(devpath);
+    close(fd);
+    return rc;
+}
+
+int ext_check(blkdev_t *dev)
+{
+    char *devpath;
+
+#if EXT_DEBUG
+    LOG_VOL("ext_check(%s):", dev->dev_fspath);
+#endif
+
+    devpath = blkdev_get_devpath(dev);
+
+    if (access(E2FSCK_PATH, X_OK)) {
+        LOGE("ext_check(%s): %s not found (skipping checks)",
+             devpath, E2FSCK_PATH);
+        free(devpath);
+        return 0;
+    }
+
+    char *args[5];
+
+    args[0] = E2FSCK_PATH;
+    args[1] = "-v";
+    args[2] = "-p";
+    args[3] = devpath;
+    args[4] = NULL;
+
+    int rc = logwrap(4, args);
+
+    if (rc == 0) {
+        LOG_VOL("filesystem '%s' had no errors", devpath);
+    } else if (rc == 1) {
+        LOG_VOL("filesystem '%s' had corrected errors", devpath);
+        rc = 0;
+    } else if (rc == 2) {
+        LOGE("VOL volume '%s' had corrected errors (system should be rebooted)", devpath);
+        rc = -EIO;
+    } else if (rc == 4) {
+        LOGE("VOL volume '%s' had uncorrectable errors", devpath);
+        rc = -EIO;
+    } else if (rc == 8) {
+        LOGE("Operational error while checking volume '%s'", devpath);
+        rc = -EIO;
+    } else {
+        LOGE("Unknown e2fsck exit code (%d)", rc);
+        rc = -EIO;
+    }
+    free(devpath);
+    return rc;
+}
+
+int ext_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode)
+{
+#if EXT_DEBUG
+    LOG_VOL("ext_mount(%s, %s, %d):", dev->dev_fspath, vol->mount_point, safe_mode);
+#endif
+
+    char *fs[] = { "ext3", "ext2", NULL };
+    char *devpath;
+
+    devpath = blkdev_get_devpath(dev);
+
+    int flags, rc = 0;
+
+    flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
+
+    if (safe_mode)
+        flags |= MS_SYNCHRONOUS;
+
+    if (vol->state == volstate_mounted) {
+        LOG_VOL("Remounting %s on %s, safe mode %d", devpath,
+                vol->mount_point, safe_mode);
+        flags |= MS_REMOUNT;
+    }
+ 
+    char **f;
+    for (f = fs; *f != NULL; f++) {
+        rc = mount(devpath, vol->mount_point, *f, flags, NULL);
+        if (rc && errno == EROFS) {
+            LOGE("ext_mount(%s, %s): Read only filesystem - retrying mount RO",
+                 devpath, vol->mount_point);
+            flags |= MS_RDONLY;
+            rc = mount(devpath, vol->mount_point, *f, flags, NULL);
+        }
+#if EXT_DEBUG
+        LOG_VOL("ext_mount(%s, %s): %s mount rc = %d", devpath, *f,
+                vol->mount_point, rc);
+#endif
+        if (!rc)
+            break;
+    }
+    free(devpath);
+
+    // Chmod the mount point so that its a free-for-all.
+    // (required for consistency with VFAT.. sigh)
+    if (chmod(vol->mount_point, 0777) < 0) {
+        LOGE("Failed to chmod %s (%s)", vol->mount_point, strerror(errno));
+        return -errno;
+    }
+    
+    return rc;
+}
diff --git a/vold/volmgr_ext3.h b/vold/volmgr_ext3.h
new file mode 100644
index 0000000..bfe882a
--- /dev/null
+++ b/vold/volmgr_ext3.h
@@ -0,0 +1,27 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_EXT3_H
+#define _VOLMGR_EXT3_H
+
+#include "volmgr.h"
+#include "blkdev.h"
+
+int ext_identify(blkdev_t *blkdev);
+int ext_check(blkdev_t *blkdev);
+int ext_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode);
+#endif
diff --git a/vold/volmgr_vfat.c b/vold/volmgr_vfat.c
new file mode 100644
index 0000000..1dc4c8d
--- /dev/null
+++ b/vold/volmgr_vfat.c
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+
+#include <sys/mount.h>
+
+#include "vold.h"
+#include "volmgr.h"
+#include "volmgr_vfat.h"
+#include "logwrapper.h"
+
+#define VFAT_DEBUG 0
+
+static char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
+
+int vfat_identify(blkdev_t *dev)
+{
+#if VFAT_DEBUG
+    LOG_VOL("vfat_identify(%d:%d):", dev->major, dev->minor);
+#endif
+    return 0; // XXX: Implement
+}
+
+int vfat_check(blkdev_t *dev)
+{
+    int rc;
+
+#if VFAT_DEBUG
+    LOG_VOL("vfat_check(%d:%d):", dev->major, dev->minor);
+#endif
+
+    if (access(FSCK_MSDOS_PATH, X_OK)) {
+        LOGE("vfat_check(%d:%d): %s not found (skipping checks)",
+             dev->major, dev->minor, FSCK_MSDOS_PATH);
+        return 0;
+    }
+
+#ifdef VERIFY_PASS
+    char *args[7];
+    args[0] = FSCK_MSDOS_PATH;
+    args[1] = "-v";
+    args[2] = "-V";
+    args[3] = "-w";
+    args[4] = "-p";
+    args[5] = blkdev_get_devpath(dev);
+    args[6] = NULL;
+    rc = logwrap(6, args);
+    free(args[5]);
+#else
+    char *args[6];
+    args[0] = FSCK_MSDOS_PATH;
+    args[1] = "-v";
+    args[2] = "-w";
+    args[3] = "-p";
+    args[4] = blkdev_get_devpath(dev);
+    args[5] = NULL;
+    rc = logwrap(5, args);
+    free(args[4]);
+#endif
+
+    if (rc == 0) {
+        LOG_VOL("Filesystem check completed OK");
+        return 0;
+    } else if (rc == 1) {
+        LOG_VOL("Filesystem check failed (general failure)");
+        return -EINVAL;
+    } else if (rc == 2) {
+        LOG_VOL("Filesystem check failed (invalid usage)");
+        return -EIO;
+    } else if (rc == 4) {
+        LOG_VOL("Filesystem check completed (errors fixed)");
+    } else if (rc == 8) {
+        LOG_VOL("Filesystem check failed (not a FAT filesystem)");
+        return -ENODATA;
+    } else {
+        LOG_VOL("Filesystem check failed (unknown exit code %d)", rc);
+        return -EIO;
+    }
+    return 0;
+}
+
+int vfat_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode)
+{
+    int flags, rc;
+    char *devpath;
+
+    devpath = blkdev_get_devpath(dev);
+
+#if VFAT_DEBUG
+    LOG_VOL("vfat_mount(%d:%d, %s, %d):", dev->major, dev->minor, vol->mount_point, safe_mode);
+#endif
+
+    flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+
+    if (safe_mode)
+        flags |= MS_SYNCHRONOUS;
+    if (vol->state == volstate_mounted) {
+        LOG_VOL("Remounting %d:%d on %s, safe mode %d", dev->major,
+                dev->minor, vol->mount_point, safe_mode);
+        flags |= MS_REMOUNT;
+    }
+
+    rc = mount(devpath, vol->mount_point, "vfat", flags,
+               "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+
+    if (rc && errno == EROFS) {
+        LOGE("vfat_mount(%d:%d, %s): Read only filesystem - retrying mount RO",
+             dev->major, dev->minor, vol->mount_point);
+        flags |= MS_RDONLY;
+        rc = mount(devpath, vol->mount_point, "vfat", flags,
+                   "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+    }
+
+#if VFAT_DEBUG
+    LOG_VOL("vfat_mount(%s, %d:%d): mount rc = %d", dev->major,k dev->minor,
+            vol->mount_point, rc);
+#endif
+    free (devpath);
+    return rc;
+}
diff --git a/vold/volmgr_vfat.h b/vold/volmgr_vfat.h
new file mode 100644
index 0000000..d9cf04d
--- /dev/null
+++ b/vold/volmgr_vfat.h
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_VFAT_H
+#define _VOLMGR_VFAT_H
+
+#include "volmgr.h"
+#include "blkdev.h"
+
+
+
+int vfat_identify(blkdev_t *blkdev);
+int vfat_check(blkdev_t *blkdev);
+int vfat_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode);
+#endif