auto import from //depot/cupcake/@135843
diff --git a/mountd/ASEC.c b/mountd/ASEC.c
new file mode 100644
index 0000000..a6aab9c
--- /dev/null
+++ b/mountd/ASEC.c
@@ -0,0 +1,774 @@
+/*
+ * 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.
+ */
+
+/*
+** Android Secure External Cache 
+*/
+
+#include "mountd.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 <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <linux/dm-ioctl.h>
+#include <linux/loop.h>
+
+#include <cutils/properties.h>
+#include <cutils/misc.h>
+
+#include "ASEC.h"
+
+//#define MODULE_FAILURE_IS_FATAL
+
+extern int init_module(void *, unsigned long, const char *);
+extern int delete_module(const char *, unsigned int);
+
+struct asec_context
+{
+    char *name;           // Device mapper volume name
+    char *srcPath;        // Path to the source (original) mount
+    char *backingFile;    // Name of the image file
+    unsigned int sectors; // Number of sectors
+    char *dstPath;        // Destination mount point
+    char *crypt;          // Crypt options
+
+    boolean needs_format;
+    boolean started;
+    int cacheFd;
+    int lo_num;
+    int dm_num;
+    unsigned char key[16];
+};
+
+static const char *MODULES[] = { "dm_mod", "crypto", "crypto_algapi", "crypto_blkcipher", 
+                                 "cryptomgr", "dm_crypt", "jbd",  
+                                 "twofish_common", "twofish", "cbc",
+                                 "mbcache", "ext3",
+                                 NULL };
+static const char KEY_PATH[] = "/data/system/asec.key";
+static const char MODULE_PATH[] = "/system/lib/modules";
+static const char MKE2FS_PATH[] = "/system/bin/mke2fs";
+static const char E2FSCK_PATH[] = "/system/bin/e2fsck";
+
+boolean AsecIsStarted(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+
+    return ctx->started;
+}
+
+const char *AsecMountPoint(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+
+    return ctx->dstPath;
+}
+
+static boolean AsecIsEnabled()
+{
+    char value[PROPERTY_VALUE_MAX];
+    int  enabled;
+
+    property_get(ASEC_ENABLED, value, "0");
+
+    if (atoi(value) == 1)
+        return true;
+    return false;
+}
+
+void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile,
+               const char *Size, const char *DstPath, const char *Crypt)
+{
+    struct asec_context *ctx;
+
+    if (!AsecIsEnabled())
+        return NULL;
+
+    LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n",
+             Name, SrcPath, BackingFile, Size, DstPath, Crypt);
+
+    if (!Name || !SrcPath || !BackingFile || !Size || !DstPath || !Crypt) {
+        LOG_ERROR("AsecInit(): Invalid arguments\n");
+        return NULL;
+    }
+
+    if (!(ctx = malloc(sizeof(struct asec_context)))) {
+        LOG_ERROR("AsecInit(): Out of memory\n");
+        return NULL;
+    }
+
+    memset(ctx, 0, sizeof(struct asec_context));
+    ctx->name = strdup(Name);
+    ctx->srcPath = strdup(SrcPath);
+    ctx->backingFile = strdup(BackingFile);
+    ctx->sectors = atoi(Size);
+    ctx->dstPath = strdup(DstPath);
+    ctx->crypt = strdup(Crypt);
+    return ctx;
+}
+
+void AsecDeinit(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+
+    free(ctx->name);
+    free(ctx->srcPath);
+    free(ctx->backingFile);
+    free(ctx->dstPath);
+    free(ctx->crypt);
+
+    free(ctx);
+}
+
+static int AsecLoadModules()
+{
+    int i;
+
+    for (i = 0; MODULES[i] != NULL; i++) {
+	const char *moduleName = MODULES[i];
+        char moduleFile[255];
+        int rc = 0;
+        void *module;
+        unsigned int size;
+
+        sprintf(moduleFile, "%s/%s.ko", MODULE_PATH, moduleName);
+        module = load_file(moduleFile, &size);
+        if (!module) {
+            LOG_ERROR("Failed to load module %s (%d)\n", moduleFile, errno);
+            return -1;
+        }
+
+        rc = init_module(module, size, "");
+        free(module);
+        if (rc && errno != EEXIST) {
+            LOG_ERROR("Failed to init module %s (%d)\n", moduleFile, errno);
+            return -errno;
+        }
+    }
+    return 0;
+}
+
+static int AsecUnloadModules()
+{
+    int i, j, rc;
+
+    for (i = 0; MODULES[i] != NULL; i++);
+
+    for (j = (i - 1); j >= 0; j--) {
+	const char *moduleName = MODULES[j];
+        int maxretry = 10;
+        while(maxretry-- > 0) {
+            rc = delete_module(moduleName, O_NONBLOCK | O_EXCL);
+            if (rc < 0 && errno == EAGAIN)
+                usleep(500000);
+            else
+                break;
+        }
+        if (rc != 0) {
+            LOG_ERROR("Failed to unload module %s\n", moduleName);
+            return -errno;
+        }
+    }
+    return 0;
+}
+
+static int AsecGenerateKey(struct asec_context *ctx)
+{
+    LOG_ASEC("AsecGenerateKey():\n");
+
+    memset((void *) ctx->key, 0x69, sizeof(ctx->key));
+    return 0;
+}
+
+static int AsecLoadGenerateKey(struct asec_context *ctx)
+{
+    int fd;
+    int rc = 0;
+
+    if ((fd = open(KEY_PATH, O_RDWR | O_CREAT, 0600)) < 0) {
+        LOG_ERROR("Error opening / creating keyfile (%d)\n", errno);
+        return -errno;
+    }
+
+    if (read(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) {
+        LOG_ASEC("Generating key\n");
+        if ((rc = AsecGenerateKey(ctx)) < 0) {
+            LOG_ERROR("Error generating key (%d)\n", rc);
+            goto out;
+        }
+        if (write(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) {
+            LOG_ERROR("Error writing keyfile (%d)\n", errno);
+            rc = -1;
+            goto out;
+        }
+    }
+    
+ out:
+    close (fd);
+    return rc;
+}
+
+static int AsecFormatFilesystem(struct asec_context *ctx)
+{
+    char cmdline[255];
+    int rc;
+
+    sprintf(cmdline,
+            "%s -b 4096 -m 1 -j -L \"%s\" /dev/block/dm-%d",
+            MKE2FS_PATH, ctx->name, ctx->dm_num);
+
+    LOG_ASEC("Formatting filesystem (%s)\n", cmdline);
+    // XXX: PROTECT FROM VIKING KILLER
+    if ((rc = system(cmdline)) < 0) {
+        LOG_ERROR("Error executing format command (%d)\n", errno);
+        return -errno;
+    }
+
+    rc = WEXITSTATUS(rc);
+
+    if (!rc) {
+        LOG_ASEC("Format completed\n");
+    } else {
+        LOG_ASEC("Format failed (%d)\n", rc);
+    }
+
+    return rc;
+}
+
+static int AsecCheckFilesystem(struct asec_context *ctx)
+{
+    char cmdline[255];
+    int rc;
+
+    sprintf(cmdline, "%s -p /dev/block/dm-%d", E2FSCK_PATH, ctx->dm_num);
+
+    LOG_ASEC("Checking filesystem (%s)\n", cmdline);
+    // XXX: PROTECT FROM VIKING KILLER
+    if ((rc = system(cmdline)) < 0) {
+        LOG_ERROR("Error executing check command (%d)\n", errno);
+        return -errno;
+    }
+
+    rc = WEXITSTATUS(rc);
+
+    if (rc == 0) {
+        LOG_ASEC("ASEC volume '%s' had no errors\n", ctx->name);
+    } else if (rc == 1) {
+        LOG_ASEC("ASEC volume '%s' had corrected errors\n", ctx->name);
+        rc = 0;
+    } else if (rc == 2) {
+        LOG_ERROR("ASEC volume '%s' had corrected errors (system should be rebooted)\n", ctx->name);
+    } else if (rc == 4) {
+        LOG_ERROR("ASEC volume '%s' had uncorrectable errors\n", ctx->name);
+    } else if (rc == 8) {
+        LOG_ERROR("Operational error while checking volume '%s'\n", ctx->name);
+    } else {
+        LOG_ERROR("Unknown e2fsck exit code (%d)\n", rc);
+    }
+    return rc;
+}
+
+static int AsecOpenCreateCache(struct asec_context *ctx)
+{
+    char filepath[255];
+
+    sprintf(filepath, "%s/%s", ctx->srcPath, ctx->backingFile);
+
+    if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) {
+        if (errno == ENOENT) {
+            int rc = 0;
+
+            LOG_ASEC("Creating cache file (%u sectors)\n", ctx->sectors);
+            if ((ctx->cacheFd = creat(filepath, 0600)) < 0) {
+                LOG_ERROR("Error creating cache (%d)\n", errno);
+                return -errno;
+            }
+            if (ftruncate(ctx->cacheFd, ctx->sectors * 512) < 0) {
+                LOG_ERROR("Error truncating cache (%d)\n", errno);
+                close(ctx->cacheFd);
+                unlink(filepath);
+                return -errno;
+            }
+            LOG_ASEC("Cache created (%u sectors) \n", ctx->sectors);
+            close(ctx->cacheFd); // creat() is WRONLY
+           
+            if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) {
+               LOG_ERROR("Error opening cache file (%d)\n", errno);
+                close(ctx->cacheFd);
+                unlink(filepath);
+                return -errno;
+            }
+
+            ctx->needs_format = 1;
+        } else
+            return -errno;
+    } else {
+        struct stat stat_buf;
+
+        if (fstat(ctx->cacheFd, &stat_buf) < 0) {
+            LOG_ERROR("Failed to fstat cache (%d)\n", errno);
+            close(ctx->cacheFd);
+            return -errno;
+        }
+        if (stat_buf.st_size != ctx->sectors * 512) {
+            LOG_ERROR("Cache size %lld != configured size %u\n",
+                      stat_buf.st_size, ctx->sectors * 512);
+        }
+
+        // XXX: Verify volume label matches ctx->name
+    }
+
+    return 0;
+}
+
+static void AsecCloseCache(struct asec_context *ctx)
+{
+    close(ctx->cacheFd);
+}
+
+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 asec_context *ctx, 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(ctx->key); i++) {
+        char tmp[8];
+
+        sprintf(tmp, "%02x", ctx->key[i]);
+        strcat(key, tmp);
+    }
+
+    // XXX: Handle ctx->crypt 
+    sprintf(params, "twofish %s 0 /dev/block/loop%d 0", key, ctx->lo_num);
+ 
+    if (len < min_size)
+        len = min_size;
+
+    if (!(buffer = malloc(len))) {
+        LOG_ERROR("Unable to allocate memory\n");
+        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, ctx->name, sizeof(io->name));
+
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = ctx->sectors;
+    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 FindNextAvailableDm()
+{
+    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;
+    }
+
+    LOG_ERROR("Out of device mapper numbers\n");
+    return -1;
+}
+
+static int AsecCreateDeviceMapping(struct asec_context *ctx)
+{
+    struct dm_ioctl       *io;
+    int                   dmFd;
+    int                   rc = 0;
+
+    ctx->dm_num = FindNextAvailableDm();
+
+    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOG_ERROR("Error opening device mapper (%d)\n", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(ctx, 0))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        close(dmFd);
+        return -ENOMEM;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_CREATE, io)) < 0) {
+        LOG_ERROR("device-mapper create ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    } 
+
+    free(io);
+
+    if (!(io = _dm_ioctl_setup(ctx, DM_STATUS_TABLE_FLAG))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+ 
+    if ((rc = ioctl(dmFd, DM_TABLE_LOAD, io)) < 0) {
+        LOG_ERROR("device-mapper load ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+    free(io);
+ 
+    if (!(io = _dm_ioctl_setup(ctx, 0))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_SUSPEND, io)) < 0) {
+        LOG_ERROR("device-mapper resume ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+out_free:
+    free (io);
+out_nofree:
+    close (dmFd);
+    return rc;
+}
+
+static int AsecDestroyDeviceMapping(struct asec_context *ctx)
+{
+    struct dm_ioctl       *io;
+    int                   dmFd;
+    int                   rc = 0;
+
+    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOG_ERROR("Error opening device mapper (%d)\n", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(ctx, DM_PERSISTENT_DEV_FLAG))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) {
+        LOG_ERROR("device-mapper remove ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    } 
+
+out_free:
+    free (io);
+out_nofree:
+    close (dmFd);
+    return rc;
+}
+
+static int AsecMountCache(struct asec_context *ctx)
+{
+    int flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
+    char devname[255];
+
+    if (access(ctx->dstPath, R_OK)) {
+        LOG_ERROR("Destination mount point '%s' unavailable (%d)\n", ctx->dstPath, errno);
+        return -errno;
+    }
+
+    sprintf(devname, "/dev/block/dm-%d", ctx->dm_num);
+
+    if (mount(devname, ctx->dstPath, "ext3", flags, NULL)) {
+        LOG_ERROR("ASEC mount failed (%d)\n", errno);
+        return -errno;
+    }
+    
+    return 0;
+}
+
+static int AsecUnmountCache(struct asec_context *ctx)
+{
+    if (umount(ctx->dstPath)) {
+        if (errno == EBUSY) {
+            LOG_ASEC("ASEC volume '%s' still busy\n", ctx->name);
+        } else {
+            LOG_ERROR("ASEC umount failed (%d)\n", errno);
+        }
+        return -errno;
+    }
+    LOG_ASEC("ASEC volume '%s' unmounted\n", ctx->name);
+    return 0;
+}
+
+static int FindNextAvailableLoop()
+{
+    int i;
+
+    for (i = 0; i < MAX_LOOP; i++) {
+        struct loop_info info;
+        char devname[255];
+        int fd;
+
+        sprintf(devname, "/dev/block/loop%d", i);
+
+        if ((fd = open(devname, O_RDONLY)) < 0) {
+            LOG_ERROR("Unable to open %s (%d)\n", devname, errno);
+            return -errno;
+        }
+
+        if (ioctl(fd, LOOP_GET_STATUS, &info) < 0) {
+            close(fd);
+
+            if (errno == ENXIO)
+                return i;
+
+            LOG_ERROR("Unable to get loop status for %s (%d)\n", devname, errno);
+            return -errno;
+        }
+        close(fd);
+    }
+    return -ENXIO;
+}
+
+static int AsecCreateLoop(struct asec_context *ctx)
+{
+    char devname[255];
+    int device_fd;
+    int rc = 0;
+
+    ctx->lo_num = FindNextAvailableLoop();
+    if (ctx->lo_num < 0) {
+        LOG_ERROR("No loop devices available\n");
+        return -ENXIO;
+    }
+
+    sprintf(devname, "/dev/block/loop%d", ctx->lo_num);
+    device_fd = open(devname, O_RDWR);
+    if (device_fd < 0) {
+        LOG_ERROR("failed to open loop device (%d)\n", errno);
+        return -errno;
+    }
+
+    if (ioctl(device_fd, LOOP_SET_FD, ctx->cacheFd) < 0) {
+        LOG_ERROR("loop_set_fd ioctl failed (%d)\n", errno);
+        rc = -errno;
+    }
+    close(device_fd);
+    return rc;
+}
+
+static int AsecDestroyLoop(struct asec_context *ctx)
+{
+    char devname[255];
+    int device_fd;
+    int rc = 0;
+
+    sprintf(devname, "/dev/block/loop%d", ctx->lo_num);
+    device_fd = open(devname, O_RDONLY);
+    if (device_fd < 0) {
+        LOG_ERROR("Failed to open loop (%d)\n", errno);
+        return -errno;
+    }
+
+    if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
+        LOG_ERROR("Failed to destroy loop (%d)\n", errno);
+        rc = -errno;
+    }
+
+    close(device_fd);
+    return rc;
+}
+
+int AsecStart(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+    char value[PROPERTY_VALUE_MAX];
+    int rc = 0;
+
+    if (!ctx)
+        return -EINVAL;
+
+    if (ctx->started)
+        return -EBUSY;
+
+    LOG_ASEC("AsecStart(%s):\n", ctx->name);
+
+    NotifyAsecState(ASEC_BUSY, ctx->dstPath);
+
+    if ((rc = AsecLoadModules()) < 0) {
+        LOG_ERROR("AsecStart: Failed to load kernel modules\n");
+#ifdef MODULE_FAILURE_IS_FATAL
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+#endif
+    }
+
+    if ((rc = AsecLoadGenerateKey(ctx))) {
+        LOG_ERROR("AsecStart: Failed to load / generate key\n");
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+    
+    if ((rc = AsecOpenCreateCache(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to open / create cache\n");
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    if ((rc = AsecCreateLoop(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to create loop\n");
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	goto fail_closecache;
+    }
+
+    if ((rc = AsecCreateDeviceMapping(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to create devmapping (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+        goto fail_destroyloop;
+    }
+    
+    if (ctx->needs_format) {
+        if ((rc = AsecFormatFilesystem(ctx))) {
+            LOG_ERROR("AsecStart: Failed to format cache (%d)\n", rc);
+            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+            goto fail_destroydm;
+        }
+        ctx->needs_format = 0;
+    } else {
+        if ((rc = AsecCheckFilesystem(ctx))) {
+            LOG_ERROR("AsecStart: Failed to check filesystem (%d)\n", rc);
+            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+            goto fail_destroydm;
+        }
+    }
+
+    if ((rc = AsecMountCache(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to mount cache (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+        goto fail_destroydm;
+    }
+    
+    NotifyAsecState(ASEC_AVAILABLE, ctx->dstPath);
+    ctx->started = true;
+
+    return rc;
+
+ fail_destroydm:
+    AsecDestroyDeviceMapping(ctx);
+ fail_destroyloop:
+    AsecDestroyLoop(ctx);
+ fail_closecache:
+    AsecCloseCache(ctx);
+    return rc;
+}
+
+int AsecStop(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+    int rc = 0;
+
+    if (!ctx->started)
+        return -EINVAL;
+
+    LOG_ASEC("AsecStop(%s):\n", ctx->name);
+
+    NotifyAsecState(ASEC_BUSY, ctx->dstPath);
+
+    if ((rc = AsecUnmountCache(ctx)) < 0) {
+        LOG_ERROR("AsecStop: Failed to unmount cache (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    if ((rc = AsecDestroyDeviceMapping(ctx)) < 0) {
+        LOG_ERROR("AsecStop: Failed to destroy devmapping (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    if ((rc = AsecDestroyLoop(ctx)) < 0) {
+        LOG_ERROR("AsecStop: Failed to destroy loop device (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    AsecCloseCache(ctx);
+ 
+    if ((rc = AsecUnloadModules()) < 0) {
+        if (rc == -EAGAIN) {
+            LOG_ASEC("AsecStop: Kernel modules still in use\n");
+        } else {
+            LOG_ERROR("AsecStop: Failed to unload kernel modules (%d)\n", rc);
+#ifdef MODULE_FAILURE_IS_FATAL
+            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	    return rc;
+#endif
+        }
+    }
+
+    ctx->started = false;
+    NotifyAsecState(ASEC_DISABLED, ctx->dstPath);
+    return rc;
+}
diff --git a/mountd/ASEC.h b/mountd/ASEC.h
new file mode 100644
index 0000000..c87b288
--- /dev/null
+++ b/mountd/ASEC.h
@@ -0,0 +1,66 @@
+#ifndef _ASEC_H
+#define _ASEC_H
+
+#define ASEC_STORES_MAX 4
+#define MAX_LOOP 8
+
+typedef enum AsecState {
+    // Feature disabled
+    ASEC_DISABLED,
+
+    // Feature enabled and operational
+    ASEC_AVAILABLE,
+
+    // Busy
+    ASEC_BUSY,
+
+    // Internal Error
+    ASEC_FAILED_INTERR,
+
+    // No media available
+    ASEC_FAILED_NOMEDIA,
+
+    // Media is corrupt
+    ASEC_FAILED_BADMEDIA,
+
+    // Key mismatch
+    ASEC_FAILED_BADKEY,
+} AsecState;
+
+/*
+ * ASEC commands
+ */
+#define ASEC_CMD_SEND_STATUS		"asec_send_status"
+#define ASEC_CMD_ENABLE			"asec_enable"
+#define ASEC_CMD_DISABLE		"asec_disable"
+
+/*
+ * ASEC events
+ */
+
+// These events correspond to the states in the AsecState enum.
+// A path to the ASEC mount point follows the colon
+#define ASEC_EVENT_DISABLED		"asec_disabled:"
+#define ASEC_EVENT_AVAILABLE		"asec_available:"
+#define ASEC_EVENT_BUSY			"asec_busy:"
+#define ASEC_EVENT_FAILED_INTERR	"asec_failed_interror:"
+#define ASEC_EVENT_FAILED_NOMEDIA	"asec_failed_nomedia"
+#define ASEC_EVENT_FAILED_BADMEDIA	"asec_failed_badmedia:"
+#define ASEC_EVENT_FAILED_BADKEY	"asec_failed_badkey:"
+
+/*
+ * System Properties
+ */
+
+#define ASEC_ENABLED			"asec.enabled"
+
+#define ASEC_STATUS			"ro.asec.status"
+#define ASEC_STATUS_DISABLED		"disabled"
+#define ASEC_STATUS_AVAILABLE		"available"
+#define ASEC_STATUS_BUSY			"busy"
+#define ASEC_STATUS_FAILED_INTERR	"internal_error"
+#define ASEC_STATUS_FAILED_NOMEDIA	"no_media"
+#define ASEC_STATUS_FAILED_BADMEDIA	"bad_media"
+#define ASEC_STATUS_FAILED_BADKEY	"bad_key"
+
+#endif
diff --git a/mountd/Android.mk b/mountd/Android.mk
new file mode 100644
index 0000000..16532fa
--- /dev/null
+++ b/mountd/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=        \
+    AutoMount.c          \
+    ProcessKiller.c      \
+    Server.c             \
+    mountd.c		 \
+    ASEC.c		 \
+    logwrapper.c
+
+LOCAL_MODULE:= mountd
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := -DCREATE_MOUNT_POINTS=0
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+# disabled - we are using vold now instead
+# include $(BUILD_EXECUTABLE)
diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c
new file mode 100644
index 0000000..12ad572
--- /dev/null
+++ b/mountd/AutoMount.c
@@ -0,0 +1,1062 @@
+/*
+ * 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 automount support
+*/
+
+#include "mountd.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/loop.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+
+#define DEVPATH    "/dev/block/"
+#define DEVPATHLENGTH 11    // strlen(DEVPATH)
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+// timeout value for poll() when retries are pending
+#define POLL_TIMEOUT    1000
+
+#define MAX_MOUNT_RETRIES   3
+#define MAX_UNMOUNT_RETRIES   5
+
+typedef enum {
+    // device is unmounted
+    kUnmounted,
+    
+    // attempting to mount device
+    kMounting,
+    
+    // device is unmounted
+    kMounted,
+    
+    // attempting to unmount device
+    // so the media can be removed
+    kUnmountingForEject,
+    
+    // attempting to mount device
+    // so it can be shared via USB mass storage
+    kUnmountingForUms,
+} MountState;
+
+typedef struct MountPoint {
+    // block device to mount
+    const char* device;
+    
+    // mount point for device
+    const char* mountPoint;
+
+    // path to the UMS driver file for specifying the block device path
+    const char* driverStorePath;
+    
+    // true if device can be shared via
+    // USB mass storage
+    boolean enableUms;
+ 
+    // Array of ASEC handles
+    void *asecHandles[ASEC_STORES_MAX];
+
+    // true if the device is being shared via USB mass storage
+    boolean umsActive;
+    
+    // current state of the mount point
+    MountState state;
+    
+    // number of mount or unmount retries so far, 
+    // when attempting to mount or unmount the device
+    int retryCount; 
+ 
+    // next in sMountPointList linked list
+    struct MountPoint* next;   
+} MountPoint;
+
+// list of our mount points (does not change after initialization)
+static MountPoint* sMountPointList = NULL;
+boolean gMassStorageEnabled = false;
+boolean gMassStorageConnected = false;
+
+static pthread_t sAutoMountThread = 0;
+static pid_t gExcludedPids[2] = {-1, -1};
+
+static const char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
+
+// number of mount points that have timeouts pending
+static int sRetriesPending = 0;
+
+// for synchronization between sAutoMountThread and the server thread
+static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
+
+// requests the USB mass_storage driver to begin or end sharing a block device
+// via USB mass storage.
+static void SetBackingStore(MountPoint* mp, boolean enable) 
+{
+    int fd;
+
+    if (!mp->driverStorePath) {
+        LOG_ERROR("no driver_store_path specified in config file for %s", mp->device);
+        return;
+    }
+
+    LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false"));
+    fd = open(mp->driverStorePath, O_WRONLY);
+    if (fd < 0)
+    {
+        LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath);
+    }
+    else
+    {
+        if (enable)
+        {
+            write(fd, mp->device, strlen(mp->device));
+            mp->umsActive = true;
+        }
+        else
+        {
+            char ch = 0;
+            write(fd, &ch, 1);
+            mp->umsActive = false;
+        }
+        close(fd);
+    }
+}
+
+static boolean ReadMassStorageState()
+{
+    FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r");
+    if (file)
+    {
+        char    buffer[20];
+        fgets(buffer, sizeof(buffer), file);
+        fclose(file);
+        return (strncmp(buffer, "online", strlen("online")) == 0);
+    }
+    else
+    {
+        LOG_ERROR("could not read initial mass storage state\n");
+        return false;
+    }
+}
+
+static boolean IsLoopMounted(const char* path)
+{
+    FILE* f;
+    int count;
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    int result = 0;
+    int path_length = strlen(path);
+       
+    f = fopen("/proc/mounts", "r");
+    if (!f) {
+        LOG_ERROR("could not open /proc/mounts\n");
+        return -1;
+    }
+
+    do {
+        count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
+        if (count == 3) {
+            if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0)
+            {
+                result = 1;
+                break;
+            }
+        }
+    } while (count == 3);
+
+    fclose(f);
+    LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result);
+    return result;
+}
+
+static int CheckFilesystem(const char *device)
+{
+    char cmdline[255];
+    int rc;
+
+    // XXX: SAN: Check for FAT signature
+    
+    int result = access(FSCK_MSDOS_PATH, X_OK);
+    if (result != 0) {
+        LOG_MOUNT("CheckFilesystem(%s): %s not found (skipping checks)\n", FSCK_MSDOS_PATH, device);
+        return 0;
+    }
+ 
+    char *args[7];
+    args[0] = FSCK_MSDOS_PATH;
+    args[1] = "-v";
+    args[2] = "-V";
+    args[3] = "-w";
+    args[4] = "-p";
+    args[5] = device;
+    args[6] = NULL;
+
+    LOG_MOUNT("Checking filesystem on %s\n", device);
+    rc = logwrap(6, args);
+  
+    // XXX: We need to be able to distinguish between a FS with an error
+    // and a block device which does not have a FAT fs at all on it
+    if (rc == 0) {
+        LOG_MOUNT("Filesystem check completed OK\n");
+        return 0;
+    } else if (rc == 1) {
+        LOG_MOUNT("Filesystem check failed (general failure)\n");
+        return -EINVAL;
+    } else if (rc == 2) {
+        LOG_MOUNT("Filesystem check failed (invalid usage)\n");
+        return -EIO;
+    } else {
+        LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc);
+        return -EIO;
+    }
+}
+
+static int DoMountDevice(const char* device, const char* mountPoint)
+{
+    LOG_MOUNT("Attempting mount of %s on %s\n", device, mountPoint);
+
+#if CREATE_MOUNT_POINTS
+    // make sure mount point exists
+    mkdir(mountPoint, 0000);
+#endif
+
+    int flags = 0;
+    
+    if (device && strncmp(device, "/dev/", 5))
+    {
+        // mount with the loop driver if device does not start with "/dev/"
+        int file_fd, device_fd;
+        
+        // FIXME - only one loop mount supported at a time
+        file_fd = open(device, O_RDWR);
+        if (file_fd < -1) {
+            LOG_ERROR("open backing file %s failed\n", device);
+            return 1;
+        }
+        device_fd = open(LOOP_DEVICE, O_RDWR);
+        if (device_fd < -1) {
+            LOG_ERROR("open %s failed", LOOP_DEVICE);
+            close(file_fd);
+            return 1;
+        }
+        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
+        {
+            LOG_ERROR("ioctl LOOP_SET_FD failed\n");
+            close(file_fd);
+            close(device_fd);
+            return 1;
+        }
+
+        close(file_fd);
+        close(device_fd);
+        device = "/dev/block/loop0";
+    }
+
+    int result = access(device, R_OK);
+    if (result) {
+	LOG_ERROR("Unable to access '%s' (%d)\n", device, errno);
+   	return -errno;
+    }
+
+#if 0
+    if ((result = CheckFilesystem(device))) {
+        LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result);
+        // XXX:  Notify framework - need a new SDCARD state for the following:
+        //       - SD cards which are not present
+        //       - SD cards with no partition table
+        //       - SD cards with no filesystem
+        //       - SD cards with bad filesystem
+        return result;
+    }
+#endif
+
+    // Extra safety measures:
+    flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+    // Also, set fmask = 711 so that files cannot be marked executable,
+    // and cannot by opened by uid 1000 (system). Similar, dmask = 700
+    // so that directories cannot be accessed by uid 1000.
+    result = mount(device, mountPoint, "vfat", flags, 
+                       "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+    if (result && errno == EROFS) {
+        LOG_ERROR("mount failed EROFS, try again read-only\n");
+        flags |= MS_RDONLY;
+        result = mount(device, mountPoint, "vfat", flags,
+                       "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+    }
+
+    if (result == 0) {
+        LOG_MOUNT("Partition %s mounted on %s\n", device, mountPoint);
+        NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
+
+        MountPoint* mp = sMountPointList;
+        while (mp) {
+            if (!strcmp(mountPoint, mp->mountPoint)) {
+                int i;
+             
+                for (i = 0; i < ASEC_STORES_MAX; i++) {
+                    if (mp->asecHandles[i] != NULL) {
+                        int a_result;
+                        if ((a_result = AsecStart(mp->asecHandles[i])) < 0) {
+                            LOG_ERROR("ASEC start failure (%d)\n", a_result);
+                        }
+                    }
+                }
+                break;
+            }
+            mp = mp -> next;
+        }
+    } else if (errno == EBUSY) {
+        LOG_MOUNT("Mount failed (already mounted)\n");
+        result = 0;
+    } else {
+#if CREATE_MOUNT_POINTS
+        rmdir(mountPoint);
+#endif
+        LOG_MOUNT("Unable to mount %s on %s\n", device, mountPoint);
+    }
+
+    return result;
+}
+
+static int DoUnmountDevice(MountPoint *mp)
+{
+    boolean loop = IsLoopMounted(mp->mountPoint);
+    int i;
+
+    for (i = 0; i < ASEC_STORES_MAX; i++) {
+        if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i]))
+            AsecStop(mp->asecHandles[i]);
+    }
+
+    int result = umount(mp->mountPoint);
+    LOG_MOUNT("umount returned %d errno: %d\n", result, errno);
+
+    if (result == 0)
+    {
+#if CREATE_MOUNT_POINTS
+        rmdir(mountPoint);
+#endif
+        NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
+    }
+
+    if (loop)
+    {
+        // free the loop device
+        int loop_fd = open(LOOP_DEVICE, O_RDONLY);
+        if (loop_fd < -1) {
+            LOG_ERROR("open loop device failed\n");
+        }
+        if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+            LOG_ERROR("ioctl LOOP_CLR_FD failed\n");
+        }
+
+        close(loop_fd);
+    }
+
+    // ignore EINVAL and ENOENT, since it usually means the device is already unmounted
+    if (result && (errno == EINVAL || errno == ENOENT))
+        result = 0;
+
+    return result;
+}
+
+static int MountPartition(const char* device, const char* mountPoint)
+{
+    char    buf[100];
+    int i;
+    
+    // attempt to mount subpartitions of the device
+    for (i = 1; i < 10; i++)
+    {
+        int rc;
+        snprintf(buf, sizeof(buf), "%sp%d", device, i);
+        rc = DoMountDevice(buf, mountPoint);
+        LOG_MOUNT("DoMountDevice(%s, %s) = %d\n", buf, mountPoint, rc);
+        if (rc == 0)
+            return 0;
+    }
+
+    return -1;
+}
+
+/*****************************************************
+ * 
+ * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
+ * 
+ *****************************************************/
+
+static void SetState(MountPoint* mp, MountState state)
+{
+    mp->state = state;
+}
+
+// Enter a state that requires retries and timeouts.
+static void SetRetries(MountPoint* mp, MountState state)
+{
+    SetState(mp, state);
+    mp->retryCount = 0;
+
+    sRetriesPending++;
+    // wake up the automounter thread if we are being called 
+    // from somewhere else with no retries pending
+    if (sRetriesPending == 1 && sAutoMountThread != 0 && 
+            pthread_self() != sAutoMountThread)
+        pthread_kill(sAutoMountThread, SIGUSR1);
+}
+
+// Exit a state that requires retries and timeouts.
+static void ClearRetries(MountPoint* mp, MountState state)
+{
+    SetState(mp, state);
+    sRetriesPending--;
+}
+
+// attempt to mount the specified mount point.
+// set up retry/timeout if it does not succeed at first.
+static void RequestMount(MountPoint* mp)
+{
+    LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
+
+    if (mp->state != kMounted && mp->state != kMounting &&
+            access(mp->device, R_OK) == 0) {
+        // try raw device first
+        if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
+            MountPartition(mp->device, mp->mountPoint) == 0)
+        {
+            SetState(mp, kMounted);
+        }
+        else 
+        {
+            SetState(mp, kMounting);
+            mp->retryCount = 0;
+            SetRetries(mp, kMounting);
+        }
+    }
+}
+
+// Force the kernel to drop all caches.
+static void DropSystemCaches(void)
+{
+    int fd;
+
+    LOG_MOUNT("Dropping system caches\n");
+    fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
+
+    if (fd > 0) {
+        char ch = 3;
+        int rc;
+
+        rc = write(fd, &ch, 1);
+        if (rc <= 0)
+            LOG_MOUNT("Error dropping caches (%d)\n", rc);
+        close(fd);
+    }
+}
+
+// attempt to unmount the specified mount point.
+// set up retry/timeout if it does not succeed at first.
+static void RequestUnmount(MountPoint* mp, MountState retryState)
+{
+    int result;
+
+    LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
+    
+    if (mp->state == kMounted)
+    {
+        SendUnmountRequest(mp->mountPoint);
+
+        // do this in case the user pulls the SD card before we can successfully unmount
+        sync();
+        DropSystemCaches();
+
+        if (DoUnmountDevice(mp) == 0) 
+        {
+            SetState(mp, kUnmounted);
+            if (retryState == kUnmountingForUms) 
+            {
+                SetBackingStore(mp, true);
+                NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+            }
+        }
+        else 
+        {
+            LOG_MOUNT("unmount failed, set retry\n");
+            SetRetries(mp, retryState);
+        }
+    } 
+    else if (mp->state == kMounting)
+    {
+        SetState(mp, kUnmounted);
+    }
+}
+
+// returns true if the mount point should be shared via USB mass storage
+static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
+{
+    return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
+}
+
+// handles changes in gMassStorageEnabled and gMassStorageConnected
+static void MassStorageStateChanged()
+{
+    MountPoint* mp = sMountPointList;
+
+    boolean enable = (gMassStorageEnabled && gMassStorageConnected);
+    LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
+    
+    while (mp)
+    {
+        if (mp->enableUms)
+        {
+            if (enable)
+            {
+                if (mp->state == kMounting)
+                    SetState(mp, kUnmounted);
+                if (mp->state == kUnmounted) 
+                {
+                    SetBackingStore(mp, true);
+                    NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+                }
+                else
+                {
+                    LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
+                    // need to successfully unmount first
+                    RequestUnmount(mp, kUnmountingForUms);
+                }
+            } else if (mp->umsActive) {
+                SetBackingStore(mp, false);
+                if (mp->state == kUnmountingForUms)
+                {
+                    ClearRetries(mp, kMounted);
+                    NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
+                }
+                else if (mp->state == kUnmounted)
+                {
+                    NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
+                    RequestMount(mp);
+                }
+            }
+        }
+
+        mp = mp->next;
+    }
+}
+
+// called when USB mass storage connected state changes
+static void HandleMassStorageOnline(boolean connected)
+{
+    if (connected != gMassStorageConnected)
+    {
+        gMassStorageConnected = connected;
+        SendMassStorageConnected(connected);
+        
+        // we automatically reset to mass storage off after USB is connected
+        if (!connected)
+            gMassStorageEnabled = false;
+    
+        MassStorageStateChanged();
+    }
+}
+
+// called when a new block device has been created
+static void HandleMediaInserted(const char* device)
+{
+    MountPoint* mp = sMountPointList;
+    
+    LOG_MOUNT("HandleMediaInserted(%s):\n", device);
+
+    while (mp)
+    {
+        // see if the device matches mount point's block device
+        if (mp->state == kUnmounted &&
+                strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0) 
+        {
+            if (MassStorageEnabledForMountPoint(mp))
+            {
+                SetBackingStore(mp, true);
+                NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+            }
+            else
+                RequestMount(mp);
+        }  
+        mp = mp->next;
+    }
+}
+
+// called when a new block device has been deleted
+static void HandleMediaRemoved(const char* device)
+{    
+    MountPoint* mp = sMountPointList;
+    while (mp)
+    {
+        if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
+        {
+            if (mp->enableUms)
+                SetBackingStore(mp, false);
+
+             if (mp->state == kMounted) 
+            {
+                RequestUnmount(mp, kUnmountingForEject);
+                NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
+            }
+            
+            NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
+            break;
+        }  
+        mp = mp->next;
+    }
+}
+
+// Handle retrying to mount or unmount devices, 
+// and handle timeout condition if we have tried too many times
+static void HandleRetries()
+{
+    MountPoint* mp = sMountPointList;
+    
+    while (mp)
+    {
+       if (mp->state == kMounting) 
+       {
+            if (MountPartition(mp->device, mp->mountPoint) == 0)
+            {
+                // mount succeeded - clear the retry for this mount point
+                ClearRetries(mp, kMounted);
+            } 
+            else 
+            {
+                mp->retryCount++;
+                if (mp->retryCount == MAX_MOUNT_RETRIES)
+                {
+                    // we failed to mount the device too many times
+                    ClearRetries(mp, kUnmounted);
+                    // notify that we failed to mount
+                    NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
+                }
+            }
+       } 
+       else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
+       {
+            if (DoUnmountDevice(mp) == 0)
+            {
+                // unmounting succeeded
+                // start mass storage, if state is kUnmountingForUms
+                if (mp->state == kUnmountingForUms)
+                {
+                    SetBackingStore(mp, true);
+                     NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+                }
+                // clear the retry for this mount point
+                ClearRetries(mp, kUnmounted);
+            } 
+            else 
+            {
+                mp->retryCount++;
+                if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
+                {
+                    // kill any processes that are preventing the device from unmounting
+                    // send SIGKILL instead of SIGTERM if the first attempt did not succeed
+                    boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES);
+                    
+                    int i;
+
+                    for (i = 0; i < ASEC_STORES_MAX; i++) {
+                        if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) {
+                            LOG_MOUNT("Killing processes for ASEC path '%s'\n",
+                                      AsecMountPoint(mp->asecHandles[i]));
+                            KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]),
+                                                       sigkill,
+                                                       gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t));
+
+                            // Now that we've killed the processes, try to stop the volume again
+                            AsecStop(mp->asecHandles[i]);
+                        }
+                    }
+
+                    // unmounting the device is failing, so start killing processes
+                    KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids, 
+                                               sizeof(gExcludedPids) / sizeof(pid_t));
+
+                }
+            }
+       } 
+        
+        mp = mp->next;
+    }
+}
+
+/*****************************************************
+ * 
+ * AUTO-MOUNTER THREAD
+ * 
+ *****************************************************/
+
+static void sigusr1_handler(int signo)
+{
+    // don't need to do anything here
+}
+
+// create a socket for listening to inotify events
+int CreateINotifySocket()
+{
+    // initialize inotify
+    int fd = inotify_init();
+
+    if (fd < 0) {
+        LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
+        return -1;
+    }
+
+    fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
+
+    return fd;
+}
+
+
+// create a socket for listening to uevents
+int CreateUEventSocket()
+{
+    struct sockaddr_nl addr;
+    int sz = 64*1024;
+    int fd;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+   fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+    if(fd < 0)
+    {
+        LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
+        return -1;
+    }
+
+    setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
+
+    if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
+        close(fd);
+        return -1;
+    }
+
+    return fd;
+}
+
+/*
+ * Automounter main event thread.
+ * This thread listens for block devices being created and deleted via inotify,
+ * and listens for changes in the USB mass storage connected/disconnected via uevents from the 
+ * power supply driver.
+ * This thread also handles retries and timeouts for requests to mount or unmount a device.
+ */
+static void* AutoMountThread(void* arg)
+{
+    int inotify_fd;
+    int uevent_fd;
+    int id;
+    struct sigaction    actions;
+
+    gExcludedPids[1] = getpid();
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = sigusr1_handler;
+    sigaction(SIGUSR1, &actions, NULL);
+    
+    // initialize inotify
+    inotify_fd = CreateINotifySocket();
+    // watch for files created and deleted in "/dev"
+    inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
+
+    // initialize uevent watcher
+    uevent_fd = CreateUEventSocket();
+    if (uevent_fd < 0) 
+    {
+        LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
+        return NULL;
+    }
+    
+    while (1)
+    {
+        struct pollfd fds[2];
+        int timeout, result;
+
+#define INOTIFY_IDX 0
+#define UEVENT_IDX  1
+    
+        fds[INOTIFY_IDX].fd = inotify_fd;
+        fds[INOTIFY_IDX].events = POLLIN;
+        fds[INOTIFY_IDX].revents = 0;
+        fds[UEVENT_IDX].fd = uevent_fd;
+        fds[UEVENT_IDX].events = POLLIN;
+        fds[UEVENT_IDX].revents = 0;
+        
+        // wait for an event or a timeout to occur.
+        // poll() can also return in response to a SIGUSR1 signal
+        timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
+        result = poll(fds, 2, timeout);
+
+        // lock the mutex while we are handling events
+        pthread_mutex_lock(&sMutex);
+
+        // handle inotify notifications for block device creation and deletion
+        if (fds[INOTIFY_IDX].revents == POLLIN)
+        {
+            struct inotify_event    event;
+            char    buffer[512];
+            int length = read(inotify_fd, buffer, sizeof(buffer));
+            int offset = 0;
+ 
+            while (length >= (int)sizeof(struct inotify_event))
+            {
+               struct inotify_event* event = (struct inotify_event *)&buffer[offset];
+               
+               if (event->mask == IN_CREATE)
+               {
+                   LOG_MOUNT("/dev/block/%s created\n", event->name);
+                   HandleMediaInserted(event->name);
+               }
+               else if (event->mask == IN_DELETE)
+               {
+                   LOG_MOUNT("/dev/block/%s deleted\n", event->name);
+                   HandleMediaRemoved(event->name);
+               }
+               
+               int size = sizeof(struct inotify_event) + event->len;
+               length -= size;
+               offset += size;
+            }
+        }
+
+        // handle uevent notifications for USB state changes
+        if (fds[UEVENT_IDX].revents == POLLIN)
+        {
+            char buffer[64*1024];
+            int count;
+            
+            count = recv(uevent_fd, buffer, sizeof(buffer), 0);
+            if (count > 0) {
+                char* s = buffer;
+                char* end = s + count;
+                char* type = NULL;
+                char* online = NULL;
+                char* switchName = NULL;
+                char* switchState = NULL;
+                                
+                while (s < end) {
+                    if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
+                        type = s + strlen("POWER_SUPPLY_TYPE=");
+                    else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
+                        online = s + strlen("POWER_SUPPLY_ONLINE=");                    
+                    else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
+                        switchName = s + strlen("SWITCH_NAME=");                    
+                    else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
+                        switchState = s + strlen("SWITCH_STATE=");                    
+                    s += (strlen(s) + 1);
+                }
+
+                // we use the usb_mass_storage switch state to tell us when USB is online
+                if (switchName && switchState && 
+                        !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
+                {
+                    LOG_MOUNT("USB online\n");
+                    HandleMassStorageOnline(true);
+                }
+                
+                // and we use the power supply state to tell us when USB is offline
+                // we can't rely on the switch for offline detection because we get false positives
+                // when USB is reenumerated by the host.
+                if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
+                {
+                    LOG_MOUNT("USB offline\n");
+                    HandleMassStorageOnline(false);
+                }
+            }
+        }
+
+       // handle retries
+       if (sRetriesPending)
+            HandleRetries();
+
+        // done handling events, so unlock the mutex
+        pthread_mutex_unlock(&sMutex);
+    }
+
+    inotify_rm_watch(inotify_fd, id);
+    close(inotify_fd);
+    close(uevent_fd);
+
+    return NULL;
+}
+
+/*****************************************************
+ * 
+ * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
+ * 
+ *****************************************************/
+
+// Called to enable or disable USB mass storage support
+void EnableMassStorage(boolean enable)
+{
+    pthread_mutex_lock(&sMutex);
+
+    LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
+    gMassStorageEnabled = enable;
+    MassStorageStateChanged();
+    pthread_mutex_unlock(&sMutex);
+ }
+
+// Called to request that the specified mount point be mounted
+void MountMedia(const char* mountPoint)
+{
+    MountPoint* mp = sMountPointList;
+ 
+    LOG_MOUNT("MountMedia(%s)\n", mountPoint);
+   
+    pthread_mutex_lock(&sMutex);
+    while (mp)
+    {
+        if (strcmp(mp->mountPoint, mountPoint) == 0)
+        {
+            if (mp->state == kUnmountingForEject)
+            {
+                // handle the case where we try to remount before we actually unmounted
+                ClearRetries(mp, kMounted);
+            }
+            
+            // don't attempt to mount if mass storage is active
+            if (!MassStorageEnabledForMountPoint(mp))
+                RequestMount(mp);
+        }
+        
+        mp = mp->next;
+    }
+    pthread_mutex_unlock(&sMutex);
+ }
+
+// Called to request that the specified mount point be unmounted
+void UnmountMedia(const char* mountPoint)
+{
+    MountPoint* mp = sMountPointList;
+    
+    pthread_mutex_lock(&sMutex);
+    while (mp)
+    {
+        if (strcmp(mp->mountPoint, mountPoint) == 0)
+            RequestUnmount(mp, kUnmountingForEject);
+        
+        mp = mp->next;
+    }
+    pthread_mutex_unlock(&sMutex);
+}
+
+boolean IsMassStorageEnabled()
+{
+    return gMassStorageEnabled;
+}
+
+boolean IsMassStorageConnected()
+{
+    return gMassStorageConnected;
+}
+
+/***********************************************
+ * 
+ * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
+ * 
+ ***********************************************/
+ 
+void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms)
+{
+    MountPoint* newMountPoint;
+    
+    LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath);
+    // add a new MountPoint to the head of our linked list
+    newMountPoint = (MountPoint *)malloc(sizeof(MountPoint));
+    newMountPoint->device = device;
+    newMountPoint->mountPoint = mountPoint;
+    newMountPoint->driverStorePath = driverStorePath;
+    newMountPoint->enableUms = enableUms;
+    newMountPoint->umsActive = false;
+    newMountPoint->state = kUnmounted;
+    newMountPoint->retryCount = 0;
+
+    // add to linked list
+    newMountPoint->next = sMountPointList;
+    sMountPointList = newMountPoint;
+    return newMountPoint;
+}
+
+int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size,
+                        const char *mount_point, const char *crypt)
+{
+    MountPoint *mp = (MountPoint *) Mp;
+    int i;
+
+    for (i = 0; i < ASEC_STORES_MAX; i++) {
+        if (!mp->asecHandles[i])
+            break;   
+    }
+
+    if (i == ASEC_STORES_MAX) {
+        LOG_ERROR("Maximum # of ASEC stores exceeded\n");
+        return -EINVAL;
+    }
+
+    if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt)))
+        return -1;
+
+    return 0;
+}
+static void MountDevices()
+{
+    MountPoint* mp = sMountPointList;
+    while (mp)
+    {
+        RequestMount(mp);
+        mp = mp->next;
+    }
+}
+
+void StartAutoMounter()
+{
+    gExcludedPids[0] = getpid();
+
+    gMassStorageConnected = ReadMassStorageState();
+    LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
+
+    MountDevices();
+    pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
+}
diff --git a/mountd/MODULE_LICENSE_APACHE2 b/mountd/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mountd/MODULE_LICENSE_APACHE2
diff --git a/mountd/NOTICE b/mountd/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/mountd/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/mountd/ProcessKiller.c b/mountd/ProcessKiller.c
new file mode 100644
index 0000000..e377774
--- /dev/null
+++ b/mountd/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 "mountd.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\n", 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\n", 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\n", 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\n", 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!\n");
+                    hit = true;
+                    break;
+                }
+            }
+
+            if (!hit) {
+                LOG_ERROR("Killing process %d\n", pid);
+                kill(pid, (sigkill ? SIGKILL : SIGTERM));
+            }
+        }
+    }
+
+    closedir(dir);
+}        
diff --git a/mountd/Server.c b/mountd/Server.c
new file mode 100644
index 0000000..64459bd
--- /dev/null
+++ b/mountd/Server.c
@@ -0,0 +1,313 @@
+/*
+ * 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 server support
+*/
+
+#include "mountd.h"
+#include "ASEC.h"
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include <pthread.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <private/android_filesystem_config.h>
+
+
+// current client file descriptor
+static int sFD = -1;
+
+// to synchronize writing to client
+static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER;
+
+// path for media that failed to mount before the runtime is connected
+static char* sDeferredUnmountableMediaPath = NULL;
+
+// last asec msg before the runtime was connected
+static char* sAsecDeferredMessage = NULL;
+static char* sAsecDeferredArgument = NULL;
+
+static int Write(const char* message)
+{
+    int result = -1;
+
+    pthread_mutex_lock(&sWriteMutex);
+    
+    LOG_SERVER("Write: %s\n", message);
+    if (sFD >= 0)
+        result = write(sFD, message, strlen(message) + 1);
+
+    pthread_mutex_unlock(&sWriteMutex); 
+    
+    return result;
+}
+
+static int Write2(const char* message, const char* data)
+{
+    int result = -1;
+
+    char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
+    if (!buffer)
+    {
+        LOG_ERROR("alloca failed in Write2\n");
+        return -1;
+    }
+
+    strcpy(buffer, message);
+    strcat(buffer, data);
+    return Write(buffer);
+}
+
+static void SendStatus()
+{
+    Write(IsMassStorageConnected() ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
+    Write(IsMassStorageEnabled() ? MOUNTD_UMS_ENABLED : MOUNTD_UMS_DISABLED);
+}
+
+static void DoCommand(const char* command)
+{
+    LOG_SERVER("DoCommand %s\n", command);
+    
+    if (strcmp(command, MOUNTD_ENABLE_UMS) == 0)
+    {
+        EnableMassStorage(true);
+        Write(MOUNTD_UMS_ENABLED);
+     }
+    else if (strcmp(command, MOUNTD_DISABLE_UMS) == 0) 
+    {
+        EnableMassStorage(false);
+        Write(MOUNTD_UMS_DISABLED);
+    }
+    else if (strcmp(command, MOUNTD_SEND_STATUS) == 0)
+    {
+        SendStatus();
+    }
+    else if (strncmp(command, MOUNTD_MOUNT_MEDIA, strlen(MOUNTD_MOUNT_MEDIA)) == 0)
+    {
+        const char* path = command + strlen(MOUNTD_MOUNT_MEDIA);
+        MountMedia(path);
+    }
+    else if (strncmp(command, MOUNTD_EJECT_MEDIA, strlen(MOUNTD_EJECT_MEDIA)) == 0)
+    {
+        const char* path = command + strlen(MOUNTD_EJECT_MEDIA);
+        UnmountMedia(path);
+    } 
+    else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) {
+        LOG_ASEC("Got ASEC_CMD_ENABLE\n");
+	// XXX: SAN: Impliment
+    }
+    else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) {
+        LOG_ASEC("Got ASEC_CMD_DISABLE\n");
+	// XXX: SAN: Impliment
+    }
+    else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) {
+        LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n");
+	// XXX: SAN: Impliment
+    }
+    else
+        LOGE("unknown command %s\n", command);
+}
+
+int RunServer()
+{
+    int socket = android_get_control_socket(MOUNTD_SOCKET);
+    if (socket < 0) {
+        LOGE("Obtaining file descriptor for socket '%s' failed: %s",
+             MOUNTD_SOCKET, strerror(errno));
+        return -1;
+    }
+
+    if (listen(socket, 4) < 0) {
+        LOGE("Unable to listen on file descriptor '%d' for socket '%s': %s",
+             socket, MOUNTD_SOCKET, strerror(errno));
+        return -1;
+    }
+
+    while (1)
+    {
+        struct sockaddr addr;
+        socklen_t alen;
+        struct ucred cred;
+        socklen_t size;
+        
+        alen = sizeof(addr);
+        sFD = accept(socket, &addr, &alen);
+        if (sFD < 0)
+            continue;
+            
+        if (sDeferredUnmountableMediaPath) {
+            NotifyMediaState(sDeferredUnmountableMediaPath, MEDIA_UNMOUNTABLE, false);
+            free(sDeferredUnmountableMediaPath);
+            sDeferredUnmountableMediaPath = NULL;
+        }
+
+        if (sAsecDeferredMessage) {
+    
+            if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0)
+                LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n");
+            free(sAsecDeferredMessage);
+            free(sAsecDeferredArgument);
+            sAsecDeferredMessage = sAsecDeferredArgument = NULL;
+        }
+
+        while (1)
+        {    
+            char    buffer[101];
+            int result = read(sFD, buffer, sizeof(buffer) - 1);
+            if (result > 0)
+            {
+                int start = 0;
+                int i;
+                // command should be zero terminated, but just in case
+                buffer[result] = 0;
+                for (i = 0; i < result; i++) 
+                {
+                    if (buffer[i] == 0) 
+                    {
+                        DoCommand(buffer + start);
+                        start = i + 1;
+                    }                   
+                }
+            }
+            else
+            {
+                close(sFD);
+                sFD = -1;
+                break;
+            }
+        }
+    }  
+
+    // should never get here
+    return 0;
+}
+
+void SendMassStorageConnected(boolean connected)
+{
+    Write(connected ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
+}
+
+void SendUnmountRequest(const char* path)
+{
+    Write2(MOUNTD_REQUEST_EJECT, path);
+}
+
+void NotifyAsecState(AsecState state, const char *argument)
+{
+    const char *event = NULL;
+    const char *status = NULL;
+    boolean deferr = true;;
+
+    switch (state) {
+        case ASEC_DISABLED:
+            event = ASEC_EVENT_DISABLED;
+            status = ASEC_STATUS_DISABLED;
+            break;
+        case ASEC_AVAILABLE:
+            event = ASEC_EVENT_AVAILABLE;
+            status = ASEC_STATUS_AVAILABLE;
+            break;
+        case ASEC_BUSY:
+            event = ASEC_EVENT_BUSY;
+            status = ASEC_STATUS_BUSY;
+            deferr = false;
+            break;
+        case ASEC_FAILED_INTERR:
+            event = ASEC_EVENT_FAILED_INTERR;
+            status = ASEC_STATUS_FAILED_INTERR;
+            break;
+        case ASEC_FAILED_NOMEDIA:
+            event = ASEC_EVENT_FAILED_NOMEDIA;
+            status = ASEC_STATUS_FAILED_NOMEDIA;
+            break;
+        case ASEC_FAILED_BADMEDIA:
+            event = ASEC_EVENT_FAILED_BADMEDIA;
+            status = ASEC_STATUS_FAILED_BADMEDIA;
+            break;
+        case ASEC_FAILED_BADKEY:
+            event = ASEC_EVENT_FAILED_BADKEY;
+            status = ASEC_STATUS_FAILED_BADKEY;
+            break;
+        default:
+            LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state);
+            return;
+    }
+
+    property_set(ASEC_STATUS, status);
+
+    int result = Write2(event, argument);
+    if ((result < 0) && deferr) {
+        if (sAsecDeferredMessage) 
+            free(sAsecDeferredMessage);
+        sAsecDeferredMessage = strdup(event);
+        if (sAsecDeferredArgument)
+            free(sAsecDeferredArgument);
+        sAsecDeferredArgument = strdup(argument);
+        LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument);
+    }
+}
+
+void NotifyMediaState(const char* path, MediaState state, boolean readOnly)
+{
+    const char* event = NULL;
+    const char* propertyValue = NULL;
+    
+    switch (state) {
+        case MEDIA_REMOVED:
+            event = MOUNTD_MEDIA_REMOVED;
+            propertyValue = EXTERNAL_STORAGE_REMOVED;
+            break;
+        case MEDIA_UNMOUNTED:
+            event = MOUNTD_MEDIA_UNMOUNTED;
+            propertyValue = EXTERNAL_STORAGE_UNMOUNTED;
+            break;
+        case MEDIA_MOUNTED:
+            event = (readOnly ? MOUNTD_MEDIA_MOUNTED_READ_ONLY : MOUNTD_MEDIA_MOUNTED);
+             propertyValue = (readOnly ? EXTERNAL_STORAGE_MOUNTED_READ_ONLY : EXTERNAL_STORAGE_MOUNTED);
+           break;
+        case MEDIA_SHARED:
+            event = MOUNTD_MEDIA_SHARED;
+            propertyValue = EXTERNAL_STORAGE_SHARED;
+            break;
+        case MEDIA_BAD_REMOVAL:
+            event = MOUNTD_MEDIA_BAD_REMOVAL;
+            propertyValue = EXTERNAL_STORAGE_BAD_REMOVAL;
+            break;
+        case MEDIA_UNMOUNTABLE:
+            event = MOUNTD_MEDIA_UNMOUNTABLE;
+            propertyValue = EXTERNAL_STORAGE_UNMOUNTABLE;
+            break;
+        default:
+            LOG_ERROR("unknown MediaState %d in NotifyMediaState\n", state);
+            return;
+    }
+    
+    property_set(EXTERNAL_STORAGE_STATE, propertyValue);
+    int result = Write2(event, path);
+    if (result < 0 && state == MEDIA_UNMOUNTABLE) {
+    
+        // if we cannot communicate with the runtime, defer this message until the runtime is available
+        sDeferredUnmountableMediaPath = strdup(path);
+    }
+}
diff --git a/mountd/logwrapper.c b/mountd/logwrapper.c
new file mode 100644
index 0000000..69606ab
--- /dev/null
+++ b/mountd/logwrapper.c
@@ -0,0 +1,154 @@
+/*
+ * 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\n", argv_child[0], strerror(errno));
+        exit(-1);
+    }
+}
+
+int logwrap(int argc, char* argv[])
+{
+    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\n");
+	return -errno;
+    }
+
+    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+	LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx\n");
+	return -1;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Failed to fork\n");
+        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\n");
+            return -errno;
+        }
+
+        // redirect stdout and stderr
+        close(parent_ptty);
+        dup2(child_ptty, 1);
+        dup2(child_ptty, 2);
+        close(child_ptty);
+
+        child(argc, argv);
+    } else {
+        // switch user and group to "log"
+        // this may fail if we are not root, 
+        // but in that case switching user/group is unnecessary 
+        
+      //  setgid(AID_LOG);
+      //  setuid(AID_LOG);
+
+        return parent(argv[0], parent_ptty);
+    }
+
+    return 0;
+}
diff --git a/mountd/mountd.c b/mountd/mountd.c
new file mode 100644
index 0000000..27ec8de
--- /dev/null
+++ b/mountd/mountd.c
@@ -0,0 +1,174 @@
+/*
+ * 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 main program
+*/
+
+#include "mountd.h"
+
+#include <cutils/config_utils.h>
+#include <cutils/cpu_info.h>
+#include <cutils/properties.h>
+
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
+#include <private/android_filesystem_config.h>
+
+#ifdef MOUNTD_LOG
+FILE*    logFile;
+#endif
+
+struct asec_cfg {
+    const char *name;
+    const char *backing_file;
+    const char *size;
+    const char *mount_point;
+    const char *crypt;
+};
+
+static int ProcessAsecData(cnode *node, struct asec_cfg *stores, int idx)
+{
+    cnode *child = node->first_child;
+    const char *name = NULL;
+    const char *file = NULL;
+    const char *size = NULL;
+    const char *mp = NULL;
+    const char *crypt = NULL;
+
+    LOG_ASEC("ProcessAsecData(%s, %p, %d)\n", node->name, stores, idx);
+
+    while (child) {
+        if (!strcmp(child->name, "name"))
+            name = child->value;
+        else if (!strcmp(child->name, "backing_file"))
+            file = child->value;
+        else if (!strcmp(child->name, "size"))
+            size = child->value;
+        else if (!strcmp(child->name, "mount_point"))
+            mp = child->value;
+        else if (!strcmp(child->name, "crypt"))
+            crypt = child->value;
+        child = child->next;
+    }
+
+    if (!name || !file || !size || !mp || !crypt) {
+        LOG_ERROR("Missing required token from config. Skipping ASEC volume\n");
+        return -1;
+    } else if (idx == ASEC_STORES_MAX) {
+        LOG_ERROR("Maximum # of ASEC stores already defined\n");
+        return -1;
+    }
+
+    stores[idx].name = name;
+    stores[idx].backing_file = file;
+    stores[idx].size = size;
+    stores[idx].mount_point = mp;
+    stores[idx].crypt = crypt;
+    return ++idx;
+}
+
+static void ReadConfigFile(const char* path)
+{
+    cnode* root = config_node("", "");
+    cnode* node;
+
+    config_load_file(root, path);
+    node = root->first_child;
+
+    while (node)
+    {
+        if (strcmp(node->name, "mount") == 0)
+        {
+            const char* block_device = NULL;
+            const char* mount_point = NULL;
+            const char* driver_store_path = NULL;
+            boolean enable_ums = false;
+            cnode* child = node->first_child;
+            struct asec_cfg asec_stores[ASEC_STORES_MAX];
+            int    asec_idx = 0;
+
+            memset(asec_stores, 0, sizeof(asec_stores));
+
+            while (child)
+            {
+                const char* name = child->name;
+                const char* value = child->value;
+
+                if (!strncmp(name, "asec_", 5)) {
+                     int rc = ProcessAsecData(child, asec_stores, asec_idx);
+                     if (rc < 0) {
+                         LOG_ERROR("Error processing ASEC cfg data\n");
+                     } else
+                         asec_idx = rc;
+                } else if (strcmp(name, "block_device") == 0)
+                    block_device = value;
+                else if (strcmp(name, "mount_point") == 0)
+                    mount_point = value;
+                else if (strcmp(name, "driver_store_path") == 0)
+                    driver_store_path = value;
+                else if (strcmp(name, "enable_ums") == 0 &&
+                        strcmp(value, "true") == 0)
+                    enable_ums = true;
+                
+                child = child->next;
+            }
+
+            // mount point and removable fields are optional
+            if (block_device && mount_point)
+            {
+                void *mp = AddMountPoint(block_device, mount_point, driver_store_path, enable_ums);
+                int i;
+
+                for (i = 0; i < asec_idx; i++) {
+                    AddAsecToMountPoint(mp, asec_stores[i].name, asec_stores[i].backing_file,
+                                        asec_stores[i].size, asec_stores[i].mount_point,
+                                        asec_stores[i].crypt);
+                }
+            }
+        }
+            
+        node = node->next;
+    }
+}
+
+int main(int argc, char* argv[])
+{
+    const char*     configPath = "/system/etc/mountd.conf";
+    int             i;    
+
+    for (i = 1; i < argc; i++)
+    {
+        const char* arg = argv[i];
+        
+        if (strcmp(arg, "-f") == 0)
+        {
+            if (i < argc - 1)
+                configPath = argv[++i];
+        }
+    }
+        
+    ReadConfigFile(configPath);
+    StartAutoMounter();
+    return RunServer();
+}
diff --git a/mountd/mountd.h b/mountd/mountd.h
new file mode 100644
index 0000000..c4bc91d
--- /dev/null
+++ b/mountd/mountd.h
@@ -0,0 +1,190 @@
+/*
+ * 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 MOUNTD_H__
+#define MOUNTD_H__
+
+#define LOG_TAG "mountd"
+#include "cutils/log.h"
+
+#include "ASEC.h"
+
+typedef int boolean;
+enum {
+    false = 0,
+    true = 1
+};
+
+#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
+
+// Set this for logging error messages
+#define ENABLE_LOG_ERROR
+
+// set this to log automounter events
+#define ENABLE_LOG_MOUNT
+
+// set this to log server events
+//#define ENABLE_LOG_SERVER
+
+// set this to log ASEC events
+#define ENABLE_LOG_ASEC
+
+#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_MOUNT
+#define LOG_MOUNT(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_MOUNT(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_MOUNT */
+
+#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 */
+
+
+typedef enum MediaState {
+    // no media in SD card slot
+    MEDIA_REMOVED,
+    
+    // media in SD card slot, but not mounted
+    MEDIA_UNMOUNTED,
+    
+    // media in SD card slot and mounted at its mount point
+    MEDIA_MOUNTED,
+    
+    // media in SD card slot, unmounted, and shared as a mass storage device
+    MEDIA_SHARED,
+    
+    // media was removed from SD card slot, but mount point was not unmounted
+    // this state is cleared after the mount point is unmounted
+    MEDIA_BAD_REMOVAL,
+
+    // media in SD card slot could not be mounted (corrupt file system?)
+    MEDIA_UNMOUNTABLE,
+} MediaState;
+
+// socket name for connecting to mountd
+#define MOUNTD_SOCKET         "mountd"
+
+// mountd commands
+// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
+#define MOUNTD_ENABLE_UMS     "enable_ums"
+#define MOUNTD_DISABLE_UMS    "disable_ums"
+#define MOUNTD_SEND_STATUS    "send_status"
+
+// these commands should contain a mount point following the colon
+#define MOUNTD_MOUNT_MEDIA  "mount_media:"
+#define MOUNTD_EJECT_MEDIA  "eject_media:"
+
+// mountd events
+// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
+#define MOUNTD_UMS_ENABLED              "ums_enabled"
+#define MOUNTD_UMS_DISABLED             "ums_disabled"
+#define MOUNTD_UMS_CONNECTED            "ums_connected"
+#define MOUNTD_UMS_DISCONNECTED         "ums_disconnected"
+
+// these events correspond to the states in the MediaState enum.
+// a path to the mount point follows the colon.
+#define MOUNTD_MEDIA_REMOVED            "media_removed:"
+#define MOUNTD_MEDIA_UNMOUNTED        	"media_unmounted:"
+#define MOUNTD_MEDIA_MOUNTED          	"media_mounted:"
+#define MOUNTD_MEDIA_MOUNTED_READ_ONLY  "media_mounted_ro:"
+#define MOUNTD_MEDIA_SHARED             "media_shared:"
+#define MOUNTD_MEDIA_BAD_REMOVAL        "media_bad_removal:"
+#define MOUNTD_MEDIA_UNMOUNTABLE        "media_unmountable:"
+
+// this event sent to request unmount for media mount point
+#define MOUNTD_REQUEST_EJECT            "request_eject:"
+
+// system properties
+// these must match the corresponding strings in //device/java/android/android/os/Environment.java
+#define EXTERNAL_STORAGE_STATE          "EXTERNAL_STORAGE_STATE"
+#define EXTERNAL_STORAGE_REMOVED        "removed"
+#define EXTERNAL_STORAGE_UNMOUNTED      "unmounted"
+#define EXTERNAL_STORAGE_MOUNTED        "mounted"
+#define EXTERNAL_STORAGE_MOUNTED_READ_ONLY        "mounted_ro"
+#define EXTERNAL_STORAGE_SHARED         "shared"
+#define EXTERNAL_STORAGE_BAD_REMOVAL    "bad_removal"
+#define EXTERNAL_STORAGE_UNMOUNTABLE    "unmountable"
+
+// AutoMount.c
+
+boolean IsMassStorageEnabled();
+boolean IsMassStorageConnected();
+
+void MountMedia(const char* mountPoint);
+void UnmountMedia(const char* mountPoint);
+void EnableMassStorage(boolean enable);
+
+// call this before StartAutoMounter() to add a mount point to monitor
+void *AddMountPoint(const char* device, const char* mountPoint, const char* driverStorePath,
+                    boolean enableUms);
+
+int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file,
+                        const char *size, const char *mount_point, const char *crypt);
+
+// start automounter thread
+void StartAutoMounter();
+
+// check /proc/mounts for mounted file systems, and notify mount or unmount for any that are in our automount list
+void NotifyExistingMounts();
+
+
+// ASEC.c
+
+void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile,
+               const char *Size, const char *DstPath, const char *Crypt);
+int AsecStart(void *Handle);
+int AsecStop(void *Handle);
+void AsecDeinit(void *Handle);
+boolean AsecIsStarted(void *Handle);
+const char *AsecMountPoint(void *Handle);
+
+// ProcessKiller.c
+
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, pid_t *excluded, int num_excluded);
+
+// logwrapper.c
+int logwrap(int argc, char* argv[]);
+
+// Server.c
+
+int RunServer();
+void SendMassStorageConnected(boolean connected); 
+void SendUnmountRequest(const char* path);
+void NotifyMediaState(const char* path, MediaState state, boolean readOnly);
+void NotifyAsecState(AsecState state, const char *argument);
+#endif // MOUNTD_H__