vold2: Initial support for Android Secure External Caches

Signed-off-by: San Mehat <san@google.com>
diff --git a/Android.mk b/Android.mk
index b9e63c7..9802c9e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,7 +22,8 @@
                   logwrapper.c                         \
                   ProcessKiller.c                      \
                   geom_mbr_enc.c                       \
-                  Fat.cpp
+                  Fat.cpp                              \
+                  Loop.cpp
 
 LOCAL_MODULE:= vold
 
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 702ae32..db6899d 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -16,8 +16,10 @@
 
 #include <stdlib.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <dirent.h>
 #include <errno.h>
 
 #define LOG_TAG "CommandListener"
@@ -39,6 +41,12 @@
     registerCmd(new ShareAvailableCmd());
     registerCmd(new SimulateCmd());
     registerCmd(new FormatCmd());
+    registerCmd(new CreateAsecCmd());
+    registerCmd(new FinalizeAsecCmd());
+    registerCmd(new DestroyAsecCmd());
+    registerCmd(new MountAsecCmd());
+    registerCmd(new ListAsecCmd());
+    registerCmd(new AsecPathCmd());
 }
 
 CommandListener::ListVolumesCmd::ListVolumesCmd() :
@@ -160,3 +168,142 @@
 
     return 0;
 }
+
+CommandListener::CreateAsecCmd::CreateAsecCmd() :
+                 VoldCommand("create_asec") {
+}
+
+int CommandListener::CreateAsecCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (argc != 6) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError,
+                     "Usage: create_asec <namespace-id> <size_mb> <fstype> <key> <ownerUid>",
+                     false);
+        return 0;
+    }
+
+    if (VolumeManager::Instance()->createAsec(argv[1], atoi(argv[2]),
+                                              argv[3], argv[4],
+                                              atoi(argv[5]))) {
+        cli->sendMsg(ResponseCode::OperationFailed, "Cache creation failed", true);
+    } else {
+        cli->sendMsg(ResponseCode::CommandOkay, "Cache created", false);
+    }
+
+    return 0;
+}
+
+CommandListener::FinalizeAsecCmd::FinalizeAsecCmd() :
+                 VoldCommand("finalize_asec") {
+}
+
+int CommandListener::FinalizeAsecCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (argc != 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError,
+                     "Usage: finalize_asec <namespace-id>", false);
+        return 0;
+    }
+
+    if (VolumeManager::Instance()->finalizeAsec(argv[1])) {
+        cli->sendMsg(ResponseCode::OperationFailed, "Cache finalize failed", true);
+    } else {
+        cli->sendMsg(ResponseCode::CommandOkay, "Cache finalized", false);
+    }
+    return 0;
+}
+
+CommandListener::DestroyAsecCmd::DestroyAsecCmd() :
+                 VoldCommand("destroy_asec") {
+}
+
+int CommandListener::DestroyAsecCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (argc != 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError,
+                     "Usage: destroy_asec <namespace-id>", false);
+        return 0;
+    }
+
+    if (VolumeManager::Instance()->destroyAsec(argv[1])) {
+        cli->sendMsg(ResponseCode::OperationFailed, "Destroy failed", true);
+    } else {
+        cli->sendMsg(ResponseCode::CommandOkay, "Cache Destroyed", false);
+    }
+    return 0;
+}
+
+CommandListener::MountAsecCmd::MountAsecCmd() :
+                 VoldCommand("mount_asec") {
+}
+
+int CommandListener::MountAsecCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (argc != 4) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError,
+                     "Usage: mount_asec <namespace-id> <key> <ownerUid>", false);
+        return 0;
+    }
+
+    if (VolumeManager::Instance()->mountAsec(argv[1], argv[2], atoi(argv[3]))) {
+        cli->sendMsg(ResponseCode::OperationFailed, "Mount failed", true);
+    } else {
+        cli->sendMsg(ResponseCode::CommandOkay, "Mount succeeded", false);
+    }
+    return 0;
+}
+
+CommandListener::ListAsecCmd::ListAsecCmd() :
+                 VoldCommand("list_asec") {
+
+}
+
+int CommandListener::ListAsecCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    DIR *d = opendir("/sdcard/android_secure");
+
+    if (!d) {
+        cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
+        return 0;  
+    }
+
+    struct dirent *dent;
+    while ((dent = readdir(d))) {
+        if (dent->d_name[0] == '.')
+            continue;
+        if (!strcmp(&dent->d_name[strlen(dent->d_name)-5], ".asec")) {
+            char id[255];
+            memset(id, 0, sizeof(id));
+            strncpy(id, dent->d_name, strlen(dent->d_name) -5);
+            cli->sendMsg(ResponseCode::AsecListResult, id, false);
+        }
+    }
+    closedir(d);
+    cli->sendMsg(ResponseCode::CommandOkay, "ASEC listing complete", false);
+
+    return 0;
+}
+
+CommandListener::AsecPathCmd::AsecPathCmd() :
+                 VoldCommand("asec_path") {
+}
+
+int CommandListener::AsecPathCmd::runCommand(SocketClient *cli,
+                                            int argc, char **argv) {
+    if (argc != 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError,
+                     "Usage: asec_path <namespace-id>", false);
+        return 0;
+    }
+
+    char mountPath[255];
+
+    if (VolumeManager::Instance()->getAsecMountPath(argv[1], mountPath,
+                                                    sizeof(mountPath))) {
+        cli->sendMsg(ResponseCode::OperationFailed, "Failed to get mount path", true);
+    } else {
+        cli->sendMsg(ResponseCode::AsecPathResult, mountPath, false);
+    }
+
+    return 0;
+}
diff --git a/CommandListener.h b/CommandListener.h
index 5acec5b..cc078da 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -83,6 +83,49 @@
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
+    class CreateAsecCmd : public VoldCommand {
+    public:
+        CreateAsecCmd();
+        virtual ~CreateAsecCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class FinalizeAsecCmd : public VoldCommand {
+    public:
+        FinalizeAsecCmd();
+        virtual ~FinalizeAsecCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class DestroyAsecCmd : public VoldCommand {
+    public:
+        DestroyAsecCmd();
+        virtual ~DestroyAsecCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class MountAsecCmd : public VoldCommand {
+    public:
+        MountAsecCmd();
+        virtual ~MountAsecCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class ListAsecCmd : public VoldCommand {
+    public:
+        ListAsecCmd();
+        virtual ~ListAsecCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+    class AsecPathCmd : public VoldCommand {
+    public:
+        AsecPathCmd();
+        virtual ~AsecPathCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+
 };
 
 #endif
diff --git a/Fat.cpp b/Fat.cpp
index ec1ead5..6537a68 100644
--- a/Fat.cpp
+++ b/Fat.cpp
@@ -92,12 +92,15 @@
     return 0;
 }
 
-int Fat::doMount(const char *fsPath, const char *mountPoint) {
+int Fat::doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount) {
     int rc;
     unsigned long flags;
 
     flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
 
+    flags |= (ro ? MS_RDONLY : 0);
+    flags |= (remount ? MS_REMOUNT : 0);
+
     /*
      * Note: This is a temporary hack. If the sampling profiler is enabled,
      * we make the SD card world-writable so any process can write snapshots.
@@ -168,7 +171,9 @@
     int rc;
     args[0] = MKDOSFS_PATH;
     args[1] = "-F";
-    if ((nr_sec * 512) <= ((unsigned int) (1024*1024*1024) * 2)) 
+    if ((nr_sec * 512) <= ((unsigned int) (1024*1024) * 32)) 
+            args[2] = "12";
+    else if ((nr_sec * 512) <= ((unsigned int) (1024*1024*1024) * 2)) 
             args[2] = "16";
     else
             args[2] = "32";
diff --git a/Fat.h b/Fat.h
index 174f008..e5d76e3 100644
--- a/Fat.h
+++ b/Fat.h
@@ -22,7 +22,8 @@
 class Fat {
 public:
     static int check(const char *fsPath);
-    static int doMount(const char *fsPath, const char *mountPoint);
+    static int doMount(const char *fsPath, const char *mountPoint, bool ro,
+                       bool remount);
     static int format(const char *fsPath);
 };
 
diff --git a/Loop.cpp b/Loop.cpp
new file mode 100644
index 0000000..064e553
--- /dev/null
+++ b/Loop.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include "Loop.h"
+
+int Loop::lookupActive(const char *loopFile, char *buffer, size_t len) {
+    int i;
+    int fd;
+    char filename[256];
+
+    memset(buffer, 0, len);
+
+    for (i = 0; i < LOOP_MAX; i++) {
+        struct loop_info li;
+        int rc;
+
+        sprintf(filename, "/dev/block/loop%d", i);
+
+        if ((fd = open(filename, O_RDWR)) < 0) {
+            LOGE("Unable to open %s (%s)", filename, strerror(errno));
+            return -1;
+        }
+
+        rc = ioctl(fd, LOOP_GET_STATUS, &li);
+        close(fd);
+        if (rc < 0 && errno == ENXIO) {
+            continue;
+            break;
+        }
+
+        if (rc < 0) {
+            LOGE("Unable to get loop status for %s (%s)", filename,
+                 strerror(errno));
+            return -1;
+        }
+        if (!strncmp(li.lo_name, loopFile, LO_NAME_SIZE)) {
+            break;
+        }
+    }
+
+    if (i == LOOP_MAX) {
+        errno = ENOENT;
+        return -1;
+    }
+    strncpy(buffer, filename, len -1);
+    return 0;
+}
+
+int Loop::getNextAvailable(char *buffer, size_t len) {
+    int i;
+    int fd;
+    char filename[256];
+
+    memset(buffer, 0, len);
+
+    for (i = 0; i < LOOP_MAX; i++) {
+        struct loop_info li;
+        int rc;
+
+        sprintf(filename, "/dev/block/loop%d", i);
+
+        if ((fd = open(filename, O_RDWR)) < 0) {
+            LOGE("Unable to open %s (%s)", filename, strerror(errno));
+            return -1;
+        }
+
+        rc = ioctl(fd, LOOP_GET_STATUS, &li);
+        close(fd);
+        if (rc < 0 && errno == ENXIO)
+            break;
+
+        if (rc < 0) {
+            LOGE("Unable to get loop status for %s (%s)", filename,
+                 strerror(errno));
+            return -1;
+        }
+    }
+
+    if (i == LOOP_MAX) {
+        LOGE("Exhausted all loop devices");
+        errno = ENOSPC;
+        return -1;
+    }
+    strncpy(buffer, filename, len -1);
+    return 0;
+}
+
+int Loop::create(const char *loopDevice, const char *loopFile) {
+    int fd;
+    int file_fd;
+
+    LOGD("Creating loop for file '%s' into loop device '%s'", loopFile,
+         loopDevice);
+    if ((fd = open(loopDevice, O_RDWR)) < 0) {
+        LOGE("Unable to open loop device %s (%s)", loopDevice,
+             strerror(errno));
+        return -1;
+    }
+
+    if ((file_fd = open(loopFile, O_RDWR)) < 0) {
+        LOGE("Unable to open %s (%s)", loopFile, strerror(errno));
+        close(fd);
+        return -1;
+    }
+
+    if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
+        LOGE("Error setting up loopback interface (%s)", strerror(errno));
+        close(file_fd);
+        close(fd);
+        return -1;
+    }
+
+    struct loop_info li;
+
+    memset(&li, 0, sizeof(li));
+    strncpy(li.lo_name, loopFile, LO_NAME_SIZE);
+
+    if (ioctl(fd, LOOP_SET_STATUS, &li) < 0) {
+        LOGE("Error setting loopback status (%s)", strerror(errno));
+        close(file_fd);
+        close(fd);
+        return -1;
+    }
+
+    close(fd);
+    close(file_fd);
+
+    return 0;
+}
+
+int Loop::destroyByDevice(const char *loopDevice) {
+    int device_fd;
+
+    device_fd = open(loopDevice, O_RDONLY);
+    if (device_fd < 0) {
+        LOGE("Failed to open loop (%d)", errno);
+        return -1;
+    }
+
+    if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
+        LOGE("Failed to destroy loop (%d)", errno);
+        close(device_fd);
+        return -1;
+    }
+
+    close(device_fd);
+    return 0;
+}
+
+int Loop::destroyByFile(const char *loopFile) {
+    errno = ENOSYS;
+    return -1;
+}
+
+int Loop::createImageFile(const char *file, size_t sizeMb) {
+    int fd;
+
+    LOGD("Creating ASEC image file %s (%d mb)", file, sizeMb);
+
+    if ((fd = creat(file, 0600)) < 0) {
+        LOGE("Error creating imagefile (%s)", strerror(errno));
+        return -1;
+    }
+
+    if (ftruncate(fd, (sizeMb * (1024 * 1024))) < 0) {
+        LOGE("Error truncating imagefile (%s)", strerror(errno));
+        close(fd);
+        return -1;
+    }
+    close(fd);
+    return 0;
+}
diff --git a/Loop.h b/Loop.h
new file mode 100644
index 0000000..540db8b
--- /dev/null
+++ b/Loop.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _LOOP_H
+#define _LOOP_H
+
+#include <unistd.h>
+#include <linux/loop.h>
+
+class Loop {
+public:
+    static const int LOOP_MAX = 8;
+public:
+    static int getNextAvailable(char *buffer, size_t len);
+    static int lookupActive(const char *loopFile, char *buffer, size_t len);
+  
+    static int create(const char *loopDevice, const char *loopFile);
+    static int destroyByDevice(const char *loopDevice);
+    static int destroyByFile(const char *loopFile);
+    static int createImageFile(const char *file, size_t sizeMb);
+};
+
+#endif
diff --git a/ResponseCode.h b/ResponseCode.h
index 4f7237c..bb27787 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -21,13 +21,15 @@
 public:
     // 100 series - Requestion action was initiated; expect another reply
     // before proceeding with a new command.
-    static const int ActionInitiated = 100;
+    static const int ActionInitiated  = 100;
 
     static const int VolumeListResult = 110;
+    static const int AsecListResult   = 111;
 
     // 200 series - Requested action has been successfully completed
-    static const int CommandOkay = 200;
+    static const int CommandOkay              = 200;
     static const int ShareAvailabilityResult  = 210;
+    static const int AsecPathResult           = 211;
 
     // 400 series - The command was accepted but the requested action
     // did not take place.
diff --git a/Volume.cpp b/Volume.cpp
index d234c6f..787d4cd 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -268,7 +268,7 @@
 
         LOGI("%s checks out - attempting to mount\n", devicePath);
         errno = 0;
-        if (!(rc = Fat::doMount(devicePath, getMountpoint()))) {
+        if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false))) {
             LOGI("%s sucessfully mounted for volume %s\n", devicePath,
                  getLabel());
             setState(Volume::State_Mounted);
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 462029c..f4b62c4 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -19,6 +19,10 @@
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
 #include <linux/kdev_t.h>
 
 #define LOG_TAG "Vold"
@@ -30,6 +34,8 @@
 #include "VolumeManager.h"
 #include "DirectVolume.h"
 #include "ResponseCode.h"
+#include "Loop.h"
+#include "Fat.h"
 
 VolumeManager *VolumeManager::sInstance = NULL;
 
@@ -149,6 +155,191 @@
     return v->formatVol();
 }
 
+int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
+    char mountPoint[255];
+
+    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+
+    if (!isMountpointMounted(mountPoint)) {
+        errno = ENOENT;
+        return -1;
+    } 
+    snprintf(buffer, maxlen, "/asec/%s", id);
+    return 0;
+}
+
+int VolumeManager::createAsec(const char *id, int sizeMb,
+                              const char *fstype, const char *key, int ownerUid) {
+
+    mkdir("/sdcard/android_secure", 0777);
+
+    if (lookupVolume(id)) {
+        LOGE("ASEC volume '%s' currently exists", id);
+        errno = EADDRINUSE;
+        return -1;
+    }
+
+    char asecFileName[255];
+    snprintf(asecFileName, sizeof(asecFileName),
+             "/sdcard/android_secure/%s.asec", id);
+
+    if (!access(asecFileName, F_OK)) {
+        LOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
+             asecFileName, strerror(errno));
+        errno = EADDRINUSE;
+        return -1;
+    }
+
+    if (Loop::createImageFile(asecFileName, sizeMb)) {
+        LOGE("ASEC image file creation failed (%s)", strerror(errno));
+        return -1;
+    }
+
+    char loopDevice[255];
+    if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
+        unlink(asecFileName);
+        return -1;
+    }
+
+    if (Loop::create(loopDevice, asecFileName)) {
+        LOGE("ASEC loop device creation failed (%s)", strerror(errno));
+        unlink(asecFileName);
+        return -1;
+    }
+
+    /* XXX: Start devmapper */
+
+    if (Fat::format(loopDevice)) {
+        LOGE("ASEC FAT format failed (%s)", strerror(errno));
+        Loop::destroyByDevice(loopDevice);
+        unlink(asecFileName);
+        return -1;
+    }
+
+    char mountPoint[255];
+
+    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+    if (mkdir(mountPoint, 0777)) {
+        LOGE("Mountpoint creation failed (%s)", strerror(errno));
+        Loop::destroyByDevice(loopDevice);
+        unlink(asecFileName);
+        return -1;
+    }
+
+    if (Fat::doMount(loopDevice, mountPoint, false, false)) {
+        LOGE("ASEC FAT mount failed (%s)", strerror(errno));
+        Loop::destroyByDevice(loopDevice);
+        unlink(asecFileName);
+        return -1;
+    }
+    
+    return 0;
+}
+
+int VolumeManager::finalizeAsec(const char *id) {
+    char asecFileName[255];
+    char loopDevice[255];
+    char mountPoint[255];
+
+    snprintf(asecFileName, sizeof(asecFileName),
+             "/sdcard/android_secure/%s.asec", id);
+
+    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
+        LOGE("Unable to finalize %s (%s)", id, strerror(errno));
+        return -1;
+    }
+
+    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+    if (Fat::doMount(loopDevice, mountPoint, true, true)) {
+        LOGE("ASEC finalize mount failed (%s)", strerror(errno));
+        return -1;
+    }
+
+    LOGD("ASEC %s finalized", id);
+    return 0;
+}
+
+int VolumeManager::destroyAsec(const char *id) {
+    char asecFileName[255];
+    char mountPoint[255];
+
+    snprintf(asecFileName, sizeof(asecFileName),
+             "/sdcard/android_secure/%s.asec", id);
+    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+
+    if (isMountpointMounted(mountPoint)) {
+        int i, rc;
+        for (i = 0; i < 10; i++) {
+            rc = umount(mountPoint);
+            if (!rc) {
+                break;
+            }
+            if (rc && (errno == EINVAL || errno == ENOENT)) {
+                rc = 0;
+                break;
+            }
+            LOGW("ASEC %s unmount attempt %d failed (%s)",
+                  id, i +1, strerror(errno));
+            usleep(1000 * 250);
+        }
+        if (rc) {
+            LOGE("Failed to unmount ASEC %s for destroy", id);
+            return -1;
+        }
+    }
+
+    char loopDevice[255];
+    if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
+        Loop::destroyByDevice(loopDevice);
+    }
+
+    unlink(asecFileName);
+
+    LOGD("ASEC %s destroyed", id);
+    return 0;
+}
+
+int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
+    char asecFileName[255];
+    char mountPoint[255];
+
+    snprintf(asecFileName, sizeof(asecFileName),
+             "/sdcard/android_secure/%s.asec", id);
+    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+
+    if (isMountpointMounted(mountPoint)) {
+        LOGE("ASEC %s already mounted", id);
+        errno = EBUSY;
+        return -1;
+    }
+
+    char loopDevice[255];
+    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
+        if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
+            LOGE("Unable to find loop device for ASEC mount");
+            return -1;
+        }
+
+        if (Loop::create(loopDevice, asecFileName)) {
+            LOGE("ASEC loop device creation failed (%s)", strerror(errno));
+            return -1;
+        }
+    }
+
+    if (mkdir(mountPoint, 0777)) {
+        LOGE("Mountpoint creation failed (%s)", strerror(errno));
+        return -1;
+    }
+
+    if (Fat::doMount(loopDevice, mountPoint, true, false)) {
+        LOGE("ASEC mount failed (%s)", strerror(errno));
+        return -1;
+    }
+
+    LOGD("ASEC %s mounted", id);
+    return 0;
+}
+
 int VolumeManager::mountVolume(const char *label) {
     Volume *v = lookupVolume(label);
 
@@ -334,3 +525,31 @@
     }
     return NULL;
 }
+
+bool VolumeManager::isMountpointMounted(const char *mp)
+{
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    FILE *fp;
+    char line[1024];
+
+    if (!(fp = fopen("/proc/mounts", "r"))) {
+        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
+        return false;
+    }
+
+    while(fgets(line, sizeof(line), fp)) {
+        line[strlen(line)-1] = '\0';
+        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
+        if (!strcmp(mount_path, mp)) {
+            fclose(fp);
+            return true;
+        }
+
+    }
+
+    fclose(fp);
+    return false;
+}
+
diff --git a/VolumeManager.h b/VolumeManager.h
index f686a50..a0b63ac 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -55,6 +55,12 @@
     int shareAvailable(const char *method, bool *avail);
     int simulate(const char *cmd, const char *arg);
     int formatVolume(const char *label);
+    int createAsec(const char *id, int sizeMb, const char *fstype,
+                   const char *key, int ownerUid);
+    int finalizeAsec(const char *id);
+    int destroyAsec(const char *id);
+    int mountAsec(const char *id, const char *key, int ownerUid);
+    int getAsecMountPath(const char *id, char *buffer, int maxlen);
 
     // XXX: This should be moved private once switch uevents are working
     void notifyUmsConnected(bool connected);
@@ -67,5 +73,6 @@
 private:
     VolumeManager();
     Volume *lookupVolume(const char *label);
+    bool isMountpointMounted(const char *mp);
 };
 #endif