auto import from //depot/cupcake/@135843
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);
+}