vold: Bugfixes & cleanups

  - Fix issue where container-names > 64 bytes were getting truncated in the
    kernel. lo_name is only 64 bytes in length, so we now hash the container
    id via md5
  - Add 'dump' command to dump loop and devicemapper status
  - Add 'debug' command to enable more detailed logging at runtime
  - Log vold IPC arguments (minus encryption keys)
  - Fix premature return from Loop::lookupActive() and friends

Change-Id: I0e833261a445ce9dc1a8187e5501d27daba1ca76
Signed-off-by: San Mehat <san@google.com>
diff --git a/Android.mk b/Android.mk
index 523601e..f87fec1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,7 +25,8 @@
                   Loop.cpp                             \
                   Devmapper.cpp                        \
                   ResponseCode.cpp                     \
-                  Xwarp.cpp
+                  Xwarp.cpp                            \
+                  md5.c
 
 LOCAL_MODULE:= vold
 
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 7f81cc7..b66f7cb 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -23,7 +23,7 @@
 #include <errno.h>
 #include <fcntl.h>
 
-#define LOG_TAG "CommandListener"
+#define LOG_TAG "VoldCmdListener"
 #include <cutils/log.h>
 
 #include <sysutils/SocketClient.h>
@@ -33,9 +33,12 @@
 #include "ResponseCode.h"
 #include "Process.h"
 #include "Xwarp.h"
+#include "Loop.h"
+#include "Devmapper.h"
 
 CommandListener::CommandListener() :
                  FrameworkListener("vold") {
+    registerCmd(new DumpCmd());
     registerCmd(new VolumeCmd());
     registerCmd(new AsecCmd());
     registerCmd(new ShareCmd());
@@ -43,12 +46,62 @@
     registerCmd(new XwarpCmd());
 }
 
+void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
+    char buffer[4096];
+    char *p = buffer;
+
+    memset(buffer, 0, sizeof(buffer));
+    int i;
+    for (i = 0; i < argc; i++) {
+        int len = strlen(argv[i]) + 1; // Account for space
+        if (i == argObscure) {
+            len += 2; // Account for {}
+        }
+        if (((p - buffer) + len) < (sizeof(buffer)-1)) {
+            if (i == argObscure) {
+                *p++ = '{';
+                *p++ = '}';
+                *p++ = ' ';
+                continue;
+            }
+            strcpy(p, argv[i]);
+            p+= strlen(argv[i]);
+            if (i != (argc -1)) {
+                *p++ = ' ';
+            }
+        }
+    }
+    LOGD("%s", buffer);
+}
+
+CommandListener::DumpCmd::DumpCmd() :
+                 VoldCommand("dump") {
+}
+
+int CommandListener::DumpCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    cli->sendMsg(0, "Dumping loop status", false);
+    if (Loop::dumpState(cli)) {
+        cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
+    }
+    cli->sendMsg(0, "Dumping DM status", false);
+    if (Devmapper::dumpState(cli)) {
+        cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
+    }
+
+    cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
+    return 0;
+}
+
+
 CommandListener::VolumeCmd::VolumeCmd() :
                  VoldCommand("volume") {
 }
 
 int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
                                                       int argc, char **argv) {
+    dumpArgs(argc, argv, -1);
+
     if (argc < 2) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
         return 0;
@@ -59,6 +112,8 @@
 
     if (!strcmp(argv[1], "list")) {
         return vm->listVolumes(cli);
+    } else if (!strcmp(argv[1], "debug")) {
+        vm->setDebug(true);
     } else if (!strcmp(argv[1], "mount")) {
         rc = vm->mountVolume(argv[2]);
     } else if (!strcmp(argv[1], "unmount")) {
@@ -105,6 +160,8 @@
 
 int CommandListener::ShareCmd::runCommand(SocketClient *cli,
                                                       int argc, char **argv) {
+    dumpArgs(argc, argv, -1);
+
     if (argc < 2) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
         return 0;
@@ -136,6 +193,8 @@
 
 int CommandListener::StorageCmd::runCommand(SocketClient *cli,
                                                       int argc, char **argv) {
+    dumpArgs(argc, argv, -1);
+
     if (argc < 2) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
         return 0;
@@ -194,6 +253,7 @@
     int rc = 0;
 
     if (!strcmp(argv[1], "list")) {
+        dumpArgs(argc, argv, -1);
         DIR *d = opendir(Volume::SEC_ASECDIR);
 
         if (!d) {
@@ -214,6 +274,7 @@
         }
         closedir(d);
     } else if (!strcmp(argv[1], "create")) {
+        dumpArgs(argc, argv, 5);
         if (argc != 7) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid>", false);
@@ -223,12 +284,14 @@
         unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
         rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]));
     } else if (!strcmp(argv[1], "finalize")) {
+        dumpArgs(argc, argv, -1);
         if (argc != 3) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
             return 0;
         }
         rc = vm->finalizeAsec(argv[2]);
     } else if (!strcmp(argv[1], "destroy")) {
+        dumpArgs(argc, argv, -1);
         if (argc < 3) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
             return 0;
@@ -239,6 +302,7 @@
         }
         rc = vm->destroyAsec(argv[2], force);
     } else if (!strcmp(argv[1], "mount")) {
+        dumpArgs(argc, argv, 3);
         if (argc != 5) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
@@ -246,6 +310,7 @@
         }
         rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
     } else if (!strcmp(argv[1], "unmount")) {
+        dumpArgs(argc, argv, -1);
         if (argc < 3) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
             return 0;
@@ -256,6 +321,7 @@
         }
         rc = vm->unmountAsec(argv[2], force);
     } else if (!strcmp(argv[1], "rename")) {
+        dumpArgs(argc, argv, -1);
         if (argc != 4) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Usage: asec rename <old_id> <new_id>", false);
@@ -263,6 +329,7 @@
         }
         rc = vm->renameAsec(argv[2], argv[3]);
     } else if (!strcmp(argv[1], "path")) {
+        dumpArgs(argc, argv, -1);
         if (argc != 3) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
             return 0;
@@ -276,6 +343,7 @@
         }
         return 0;
     } else {
+        dumpArgs(argc, argv, -1);
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
     }
 
diff --git a/CommandListener.h b/CommandListener.h
index 8861e38..ca4fc97 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -26,6 +26,14 @@
     virtual ~CommandListener() {}
 
 private:
+    static void dumpArgs(int argc, char **argv, int argObscure);
+
+    class DumpCmd : public VoldCommand {
+    public:
+        DumpCmd();
+        virtual ~DumpCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
 
     class VolumeCmd : public VoldCommand {
     public:
diff --git a/Devmapper.cpp b/Devmapper.cpp
index c1aa713..ec36fdb 100644
--- a/Devmapper.cpp
+++ b/Devmapper.cpp
@@ -24,12 +24,92 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 
+#include <linux/kdev_t.h>
+
 #define LOG_TAG "Vold"
 
 #include <cutils/log.h>
 
+#include <sysutils/SocketClient.h>
+
 #include "Devmapper.h"
 
+int Devmapper::dumpState(SocketClient *c) {
+
+    char *buffer = (char *) malloc(1024 * 64);
+    if (!buffer) {
+        LOGE("Error allocating memory (%s)", strerror(errno));
+        return -1;
+    }
+    memset(buffer, 0, (1024 * 64));
+
+    char *buffer2 = (char *) malloc(4096);
+    if (!buffer2) {
+        LOGE("Error allocating memory (%s)", strerror(errno));
+        free(buffer);
+        return -1;
+    }
+
+    int fd;
+    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOGE("Error opening devmapper (%s)", strerror(errno));
+        free(buffer);
+        free(buffer2);
+        return -1;
+    }
+
+    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+    ioctlInit(io, (1024 * 64), NULL, 0);
+
+    if (ioctl(fd, DM_LIST_DEVICES, io)) {
+        LOGE("DM_LIST_DEVICES ioctl failed (%s)", strerror(errno));
+        free(buffer);
+        free(buffer2);
+        close(fd);
+        return -1;
+    }
+
+    struct dm_name_list *n = (struct dm_name_list *) (((char *) buffer) + io->data_start);
+    if (!n->dev) {
+        free(buffer);
+        free(buffer2);
+        close(fd);
+        return 0;
+    }
+
+    unsigned nxt = 0;
+    do {
+        n = (struct dm_name_list *) (((char *) n) + nxt);
+
+        memset(buffer2, 0, 4096);
+        struct dm_ioctl *io2 = (struct dm_ioctl *) buffer2;
+        ioctlInit(io2, 4096, n->name, 0);
+        if (ioctl(fd, DM_DEV_STATUS, io2)) {
+            if (errno != ENXIO) {
+                LOGE("DM_DEV_STATUS ioctl failed (%s)", strerror(errno));
+            }
+            io2 = NULL;
+        }
+
+        char *tmp;
+        if (!io2) {
+            asprintf(&tmp, "%s %llu:%llu (no status available)", n->name, MAJOR(n->dev), MINOR(n->dev));
+        } else {
+            asprintf(&tmp, "%s %llu:%llu %d %d 0x%.8x %llu:%llu", n->name, MAJOR(n->dev),
+                    MINOR(n->dev), io2->target_count, io2->open_count, io2->flags, MAJOR(io2->dev),
+                            MINOR(io2->dev));
+        }
+        c->sendMsg(0, tmp, false);
+        free(tmp);
+        nxt = n->next;
+    } while (nxt);
+
+    free(buffer);
+    free(buffer2);
+    close(fd);
+    return 0;
+}
+
 void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize,
                           const char *name, unsigned flags) {
     memset(io, 0, dataSize);
@@ -39,7 +119,9 @@
     io->version[1] = 0;
     io->version[2] = 0;
     io->flags = flags;
-    strncpy(io->name, name, sizeof(io->name));
+    if (name) {
+        strncpy(io->name, name, sizeof(io->name));
+    }
 }
 
 int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
diff --git a/Devmapper.h b/Devmapper.h
index 52e2bed..54f808f 100644
--- a/Devmapper.h
+++ b/Devmapper.h
@@ -20,12 +20,16 @@
 #include <unistd.h>
 #include <linux/dm-ioctl.h>
 
+class SocketClient;
+
 class Devmapper {
 public:
     static int create(const char *name, const char *loopFile, const char *key,
                       unsigned int numSectors, char *buffer, size_t len);
     static int destroy(const char *name);
     static int lookupActive(const char *name, char *buffer, size_t len);
+    static int dumpState(SocketClient *c);
+
 private:
     static void *_align(void *ptr, unsigned int a);
     static void ioctlInit(struct dm_ioctl *io, size_t data_size,
diff --git a/Loop.cpp b/Loop.cpp
index 5e11ee2..c278418 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -23,13 +23,57 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include <linux/kdev_t.h>
+
 #define LOG_TAG "Vold"
 
 #include <cutils/log.h>
 
+#include <sysutils/SocketClient.h>
 #include "Loop.h"
 
-int Loop::lookupActive(const char *loopFile, char *buffer, size_t len) {
+int Loop::dumpState(SocketClient *c) {
+    int i;
+    int fd;
+    char filename[256];
+
+    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) {
+            if (errno != ENOENT) {
+                LOGE("Unable to open %s (%s)", filename, strerror(errno));
+            } else {
+                continue;
+            }
+            return -1;
+        }
+
+        rc = ioctl(fd, LOOP_GET_STATUS, &li);
+        close(fd);
+        if (rc < 0 && errno == ENXIO) {
+            continue;
+        }
+
+        if (rc < 0) {
+            LOGE("Unable to get loop status for %s (%s)", filename,
+                 strerror(errno));
+            return -1;
+        }
+        char *tmp = NULL;
+        asprintf(&tmp, "%s %d %d:%d %lu %d:%d %d 0x%x {%s}", filename, li.lo_number,
+                MAJOR(li.lo_device), MINOR(li.lo_device), li.lo_inode, MAJOR(li.lo_rdevice),
+                        MINOR(li.lo_rdevice), li.lo_offset, li.lo_flags, li.lo_name);
+        c->sendMsg(0, tmp, false);
+        free(tmp);
+    }
+    return 0;
+}
+
+int Loop::lookupActive(const char *id, char *buffer, size_t len) {
     int i;
     int fd;
     char filename[256];
@@ -45,6 +89,8 @@
         if ((fd = open(filename, O_RDWR)) < 0) {
             if (errno != ENOENT) {
                 LOGE("Unable to open %s (%s)", filename, strerror(errno));
+            } else {
+                continue;
             }
             return -1;
         }
@@ -53,7 +99,6 @@
         close(fd);
         if (rc < 0 && errno == ENXIO) {
             continue;
-            break;
         }
 
         if (rc < 0) {
@@ -61,7 +106,7 @@
                  strerror(errno));
             return -1;
         }
-        if (!strncmp(li.lo_name, loopFile, LO_NAME_SIZE)) {
+        if (!strncmp(li.lo_name, id, LO_NAME_SIZE)) {
             break;
         }
     }
@@ -74,7 +119,7 @@
     return 0;
 }
 
-int Loop::create(const char *loopFile, char *loopDeviceBuffer, size_t len) {
+int Loop::create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len) {
     int i;
     int fd;
     char filename[256];
@@ -142,7 +187,7 @@
     struct loop_info li;
 
     memset(&li, 0, sizeof(li));
-    strncpy(li.lo_name, loopFile, LO_NAME_SIZE);
+    strncpy(li.lo_name, id, LO_NAME_SIZE);
 
     if (ioctl(fd, LOOP_SET_STATUS, &li) < 0) {
         LOGE("Error setting loopback status (%s)", strerror(errno));
@@ -184,8 +229,6 @@
 int Loop::createImageFile(const char *file, unsigned int numSectors) {
     int fd;
 
-    LOGD("Creating image file %s (%u sectors)", file, numSectors);
-
     if ((fd = creat(file, 0600)) < 0) {
         LOGE("Error creating imagefile (%s)", strerror(errno));
         return -1;
diff --git a/Loop.h b/Loop.h
index f06f91d..e48536b 100644
--- a/Loop.h
+++ b/Loop.h
@@ -20,15 +20,19 @@
 #include <unistd.h>
 #include <linux/loop.h>
 
+class SocketClient;
+
 class Loop {
 public:
     static const int LOOP_MAX = 4096;
 public:
-    static int lookupActive(const char *loopFile, char *buffer, size_t len);
-    static int create(const char *loopFile, char *loopDeviceBuffer, size_t len);
+    static int lookupActive(const char *id, char *buffer, size_t len);
+    static int create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len);
     static int destroyByDevice(const char *loopDevice);
     static int destroyByFile(const char *loopFile);
     static int createImageFile(const char *file, unsigned int numSectors);
+
+    static int dumpState(SocketClient *c);
 };
 
 #endif
diff --git a/Volume.cpp b/Volume.cpp
index d592c5f..80a8bf1 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -100,6 +100,7 @@
 
 Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
     mVm = vm;
+    mDebug = false;
     mLabel = strdup(label);
     mMountpoint = strdup(mount_point);
     mState = Volume::State_Init;
@@ -111,6 +112,10 @@
     free(mMountpoint);
 }
 
+void Volume::setDebug(bool enable) {
+    mDebug = enable;
+}
+
 dev_t Volume::getDiskDevice() {
     return MKDEV(0, 0);
 };
@@ -184,7 +189,9 @@
     sprintf(devicePath, "/dev/block/vold/%d:%d",
             MAJOR(diskNode), MINOR(diskNode));
 
-    LOGI("Formatting volume %s (%s)", getLabel(), devicePath);
+    if (mDebug) {
+        LOGI("Formatting volume %s (%s)", getLabel(), devicePath);
+    }
     setState(Volume::State_Formatting);
 
     if (initializeMbr(devicePath)) {
@@ -396,7 +403,9 @@
 
     while(retries--) {
         if (!mount(src, dst, "", flags, NULL)) {
-            LOGD("Moved mount %s -> %s sucessfully", src, dst);
+            if (mDebug) {
+                LOGD("Moved mount %s -> %s sucessfully", src, dst);
+            }
             return 0;
         } else if (errno != EBUSY) {
             LOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno));
@@ -425,7 +434,10 @@
 int Volume::doUnmount(const char *path, bool force) {
     int retries = 10;
 
-    LOGD("Unmounting {%s}, force = %d", path, force);
+    if (mDebug) {
+        LOGD("Unmounting {%s}, force = %d", path, force);
+    }
+
     while (retries--) {
         if (!umount(path) || errno == EINVAL || errno == ENOENT) {
             LOGI("%s sucessfully unmounted", path);
diff --git a/Volume.h b/Volume.h
index c7fa996..e756160 100644
--- a/Volume.h
+++ b/Volume.h
@@ -48,6 +48,7 @@
     char *mLabel;
     char *mMountpoint;
     VolumeManager *mVm;
+    bool mDebug;
 
     /*
      * The major/minor tuple of the currently mounted filesystem.
@@ -71,6 +72,8 @@
     virtual void handleVolumeShared();
     virtual void handleVolumeUnshared();
 
+    void setDebug(bool enable);
+
 protected:
     void setState(int state);
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 8f1cb52..77dad20 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -39,6 +39,7 @@
 #include "Devmapper.h"
 #include "Process.h"
 #include "Asec.h"
+#include "md5.h"
 
 VolumeManager *VolumeManager::sInstance = NULL;
 
@@ -49,6 +50,7 @@
 }
 
 VolumeManager::VolumeManager() {
+    mDebug = false;
     mBlockDevices = new BlockDeviceCollection();
     mVolumes = new VolumeCollection();
     mActiveContainers = new AsecIdCollection();
@@ -62,6 +64,38 @@
     delete mActiveContainers;
 }
 
+char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
+    MD5_CTX ctx;
+    unsigned char sig[16];
+
+    if (len < 33) {
+        LOGE("Target hash buffer size < 33 bytes (%d)", len);
+        errno = ESPIPE;
+        return NULL;
+    }
+    MD5_Init(&ctx);
+    MD5_Update(&ctx, id, strlen(id));
+    MD5_Final(sig, &ctx);
+
+    memset(buffer, 0, len);
+
+    for (int i = 0; i < 16; i++) {
+        char tmp[3];
+        snprintf(tmp, 3, "%.02x", sig[i]);
+        strcat(buffer, tmp);
+    }
+
+    return buffer;
+}
+
+void VolumeManager::setDebug(bool enable) {
+    mDebug = enable;
+    VolumeCollection::iterator it;
+    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
+        (*it)->setDebug(enable);
+    }
+}
+
 int VolumeManager::start() {
     return 0;
 }
@@ -213,8 +247,15 @@
         return -1;
     }
 
+    char idHash[33];
+    if (!asecHash(id, idHash, sizeof(idHash))) {
+        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
+        unlink(asecFileName);
+        return -1;
+    }
+
     char loopDevice[255];
-    if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) {
+    if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
         LOGE("ASEC loop device creation failed (%s)", strerror(errno));
         unlink(asecFileName);
         return -1;
@@ -226,7 +267,7 @@
     if (strcmp(key, "none")) {
         // XXX: This is all we support for now
         sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
-        if (Devmapper::create(id, loopDevice, key, numImgSectors, dmDevice,
+        if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
                              sizeof(dmDevice))) {
             LOGE("ASEC device mapping failed (%s)", strerror(errno));
             Loop::destroyByDevice(loopDevice);
@@ -247,7 +288,7 @@
     if (sbfd < 0) {
         LOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
         if (cleanupDm) {
-            Devmapper::destroy(id);
+            Devmapper::destroy(idHash);
         }
         Loop::destroyByDevice(loopDevice);
         unlink(asecFileName);
@@ -258,7 +299,7 @@
         close(sbfd);
         LOGE("Failed to lseek for superblock (%s)", strerror(errno));
         if (cleanupDm) {
-            Devmapper::destroy(id);
+            Devmapper::destroy(idHash);
         }
         Loop::destroyByDevice(loopDevice);
         unlink(asecFileName);
@@ -269,7 +310,7 @@
         close(sbfd);
         LOGE("Failed to write superblock (%s)", strerror(errno));
         if (cleanupDm) {
-            Devmapper::destroy(id);
+            Devmapper::destroy(idHash);
         }
         Loop::destroyByDevice(loopDevice);
         unlink(asecFileName);
@@ -285,7 +326,7 @@
         if (Fat::format(dmDevice, numImgSectors)) {
             LOGE("ASEC FAT format failed (%s)", strerror(errno));
             if (cleanupDm) {
-                Devmapper::destroy(id);
+                Devmapper::destroy(idHash);
             }
             Loop::destroyByDevice(loopDevice);
             unlink(asecFileName);
@@ -298,7 +339,7 @@
             if (errno != EEXIST) {
                 LOGE("Mountpoint creation failed (%s)", strerror(errno));
                 if (cleanupDm) {
-                    Devmapper::destroy(id);
+                    Devmapper::destroy(idHash);
                 }
                 Loop::destroyByDevice(loopDevice);
                 unlink(asecFileName);
@@ -310,7 +351,7 @@
                          0, 0000, false)) {
             LOGE("ASEC FAT mount failed (%s)", strerror(errno));
             if (cleanupDm) {
-                Devmapper::destroy(id);
+                Devmapper::destroy(idHash);
             }
             Loop::destroyByDevice(loopDevice);
             unlink(asecFileName);
@@ -331,7 +372,13 @@
 
     snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
 
-    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
+    char idHash[33];
+    if (!asecHash(id, idHash, sizeof(idHash))) {
+        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
+        return -1;
+    }
+
+    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
         LOGE("Unable to finalize %s (%s)", id, strerror(errno));
         return -1;
     }
@@ -343,7 +390,9 @@
         return -1;
     }
 
-    LOGD("ASEC %s finalized", id);
+    if (mDebug) {
+        LOGD("ASEC %s finalized", id);
+    }
     return 0;
 }
 
@@ -398,6 +447,12 @@
     snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
 
+    char idHash[33];
+    if (!asecHash(id, idHash, sizeof(idHash))) {
+        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
+        return -1;
+    }
+
     if (!isMountpointMounted(mountPoint)) {
         LOGE("Unmount request for ASEC %s when not mounted", id);
         errno = EINVAL;
@@ -452,13 +507,15 @@
         LOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
     }
 
-    if (Devmapper::destroy(id) && errno != ENXIO) {
+    if (Devmapper::destroy(idHash) && errno != ENXIO) {
         LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
     }
 
     char loopDevice[255];
-    if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
+    if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
         Loop::destroyByDevice(loopDevice);
+    } else {
+        LOGW("Failed to find loop device for {%s} (%s)", asecFileName, strerror(errno));
     }
 
     AsecIdCollection::iterator it;
@@ -483,7 +540,9 @@
     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
 
     if (isMountpointMounted(mountPoint)) {
-        LOGD("Unmounting container before destroy");
+        if (mDebug) {
+            LOGD("Unmounting container before destroy");
+        }
         if (unmountAsec(id, force)) {
             LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
             return -1;
@@ -495,7 +554,9 @@
         return -1;
     }
 
-    LOGD("ASEC %s destroyed", id);
+    if (mDebug) {
+        LOGD("ASEC %s destroyed", id);
+    }
     return 0;
 }
 
@@ -512,15 +573,24 @@
         return -1;
     }
 
+    char idHash[33];
+    if (!asecHash(id, idHash, sizeof(idHash))) {
+        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
+        return -1;
+    }
     char loopDevice[255];
-    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
-        if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) {
+    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
+        if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
             LOGE("ASEC loop device creation failed (%s)", strerror(errno));
             return -1;
         }
-        LOGD("New loop device created at %s", loopDevice);
+        if (mDebug) {
+            LOGD("New loop device created at %s", loopDevice);
+        }
     } else {
-        LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
+        if (mDebug) {
+            LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
+        }
     }
 
     char dmDevice[255];
@@ -561,7 +631,9 @@
 
     close(fd);
 
-    LOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
+    if (mDebug) {
+        LOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
+    }
     if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
         LOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
         Loop::destroyByDevice(loopDevice);
@@ -571,16 +643,20 @@
     nr_sec--; // We don't want the devmapping to extend onto our superblock
 
     if (strcmp(key, "none")) {
-        if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) {
-            if (Devmapper::create(id, loopDevice, key, nr_sec,
+        if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
+            if (Devmapper::create(idHash, loopDevice, key, nr_sec,
                                   dmDevice, sizeof(dmDevice))) {
                 LOGE("ASEC device mapping failed (%s)", strerror(errno));
                 Loop::destroyByDevice(loopDevice);
                 return -1;
             }
-            LOGD("New devmapper instance created at %s", dmDevice);
+            if (mDebug) {
+                LOGD("New devmapper instance created at %s", dmDevice);
+            }
         } else {
-            LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
+            if (mDebug) {
+                LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
+            }
         }
         cleanupDm = true;
     } else {
@@ -591,7 +667,7 @@
         if (errno != EEXIST) {
             LOGE("Mountpoint creation failed (%s)", strerror(errno));
             if (cleanupDm) {
-                Devmapper::destroy(id);
+                Devmapper::destroy(idHash);
             }
             Loop::destroyByDevice(loopDevice);
             return -1;
@@ -603,14 +679,16 @@
 //                     0227, false)) {
         LOGE("ASEC mount failed (%s)", strerror(errno));
         if (cleanupDm) {
-            Devmapper::destroy(id);
+            Devmapper::destroy(idHash);
         }
         Loop::destroyByDevice(loopDevice);
         return -1;
     }
 
     mActiveContainers->push_back(strdup(id));
-    LOGD("ASEC %s mounted", id);
+    if (mDebug) {
+        LOGD("ASEC %s mounted", id);
+    }
     return 0;
 }
 
diff --git a/VolumeManager.h b/VolumeManager.h
index 5ed6f21..b523f31 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -38,6 +38,7 @@
     VolumeCollection      *mVolumes;
     AsecIdCollection      *mActiveContainers;
     bool                   mUsbMassStorageConnected;
+    bool                   mDebug;
 
 public:
     virtual ~VolumeManager();
@@ -68,6 +69,8 @@
     int renameAsec(const char *id1, const char *id2);
     int getAsecMountPath(const char *id, char *buffer, int maxlen);
 
+    void setDebug(bool enable);
+
     // XXX: This should be moved private once switch uevents are working
     void notifyUmsConnected(bool connected);
 
@@ -76,6 +79,7 @@
 
     static VolumeManager *Instance();
 
+    static char *asecHash(const char *id, char *buffer, size_t len);
 private:
     VolumeManager();
     Volume *lookupVolume(const char *label);
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..3b483f1
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden). 
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ *
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+/* $Heimdal: hash.h,v 1.1 1999/03/22 19:16:25 joda Exp $
+   $NetBSD: hash.h,v 1.1.1.3 2002/09/12 12:41:42 joda Exp $ */
+
+/* stuff in common between md4, md5, and sha1 */
+
+#ifndef __hash_h__
+#define __hash_h__
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef min
+#define min(a,b) (((a)>(b))?(b):(a))
+#endif
+
+/* Vector Crays doesn't have a good 32-bit type, or more precisely,
+   int32_t as defined by <bind/bitypes.h> isn't 32 bits, and we don't
+   want to depend in being able to redefine this type.  To cope with
+   this we have to clamp the result in some places to [0,2^32); no
+   need to do this on other machines.  Did I say this was a mess?
+   */
+
+#ifdef _CRAY
+#define CRAYFIX(X) ((X) & 0xffffffff)
+#else
+#define CRAYFIX(X) (X)
+#endif
+
+static inline u_int32_t
+cshift (u_int32_t x, unsigned int n)
+{
+    x = CRAYFIX(x);
+    return CRAYFIX((x << n) | (x >> (32 - n)));
+}
+
+#endif /* __hash_h__ */
diff --git a/md5.c b/md5.c
new file mode 100644
index 0000000..087786f
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+__RCSID("$Heimdal: md5.c,v 1.15 2001/01/29 04:33:44 assar Exp $"
+        "$NetBSD: md5.c,v 1.1.1.4 2002/09/12 12:41:42 joda Exp $");
+#endif
+
+#include "md5.h"
+#include "hash.h"
+
+#define A m->counter[0]
+#define B m->counter[1]
+#define C m->counter[2]
+#define D m->counter[3]
+#define X data
+
+void
+MD5_Init (struct md5 *m)
+{
+  m->sz[0] = 0;
+  m->sz[1] = 0;
+  D = 0x10325476;
+  C = 0x98badcfe;
+  B = 0xefcdab89;
+  A = 0x67452301;
+}
+
+#define F(x,y,z) CRAYFIX((x & y) | (~x & z))
+#define G(x,y,z) CRAYFIX((x & z) | (y & ~z))
+#define H(x,y,z) (x ^ y ^ z)
+#define I(x,y,z) CRAYFIX(y ^ (x | ~z))
+
+#define DOIT(a,b,c,d,k,s,i,OP) \
+a = b + cshift(a + OP(b,c,d) + X[k] + (i), s)
+
+#define DO1(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,F)
+#define DO2(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,G)
+#define DO3(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,H)
+#define DO4(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,I)
+
+static inline void
+calc (struct md5 *m, u_int32_t *data)
+{
+  u_int32_t AA, BB, CC, DD;
+
+  AA = A;
+  BB = B;
+  CC = C;
+  DD = D;
+
+  /* Round 1 */
+
+  DO1(A,B,C,D,0,7,0xd76aa478);
+  DO1(D,A,B,C,1,12,0xe8c7b756);
+  DO1(C,D,A,B,2,17,0x242070db);
+  DO1(B,C,D,A,3,22,0xc1bdceee);
+
+  DO1(A,B,C,D,4,7,0xf57c0faf);
+  DO1(D,A,B,C,5,12,0x4787c62a);
+  DO1(C,D,A,B,6,17,0xa8304613);
+  DO1(B,C,D,A,7,22,0xfd469501);
+
+  DO1(A,B,C,D,8,7,0x698098d8);
+  DO1(D,A,B,C,9,12,0x8b44f7af);
+  DO1(C,D,A,B,10,17,0xffff5bb1);
+  DO1(B,C,D,A,11,22,0x895cd7be);
+
+  DO1(A,B,C,D,12,7,0x6b901122);
+  DO1(D,A,B,C,13,12,0xfd987193);
+  DO1(C,D,A,B,14,17,0xa679438e);
+  DO1(B,C,D,A,15,22,0x49b40821);
+
+  /* Round 2 */
+
+  DO2(A,B,C,D,1,5,0xf61e2562);
+  DO2(D,A,B,C,6,9,0xc040b340);
+  DO2(C,D,A,B,11,14,0x265e5a51);
+  DO2(B,C,D,A,0,20,0xe9b6c7aa);
+
+  DO2(A,B,C,D,5,5,0xd62f105d);
+  DO2(D,A,B,C,10,9,0x2441453);
+  DO2(C,D,A,B,15,14,0xd8a1e681);
+  DO2(B,C,D,A,4,20,0xe7d3fbc8);
+
+  DO2(A,B,C,D,9,5,0x21e1cde6);
+  DO2(D,A,B,C,14,9,0xc33707d6);
+  DO2(C,D,A,B,3,14,0xf4d50d87);
+  DO2(B,C,D,A,8,20,0x455a14ed);
+
+  DO2(A,B,C,D,13,5,0xa9e3e905);
+  DO2(D,A,B,C,2,9,0xfcefa3f8);
+  DO2(C,D,A,B,7,14,0x676f02d9);
+  DO2(B,C,D,A,12,20,0x8d2a4c8a);
+
+  /* Round 3 */
+
+  DO3(A,B,C,D,5,4,0xfffa3942);
+  DO3(D,A,B,C,8,11,0x8771f681);
+  DO3(C,D,A,B,11,16,0x6d9d6122);
+  DO3(B,C,D,A,14,23,0xfde5380c);
+
+  DO3(A,B,C,D,1,4,0xa4beea44);
+  DO3(D,A,B,C,4,11,0x4bdecfa9);
+  DO3(C,D,A,B,7,16,0xf6bb4b60);
+  DO3(B,C,D,A,10,23,0xbebfbc70);
+
+  DO3(A,B,C,D,13,4,0x289b7ec6);
+  DO3(D,A,B,C,0,11,0xeaa127fa);
+  DO3(C,D,A,B,3,16,0xd4ef3085);
+  DO3(B,C,D,A,6,23,0x4881d05);
+
+  DO3(A,B,C,D,9,4,0xd9d4d039);
+  DO3(D,A,B,C,12,11,0xe6db99e5);
+  DO3(C,D,A,B,15,16,0x1fa27cf8);
+  DO3(B,C,D,A,2,23,0xc4ac5665);
+
+  /* Round 4 */
+
+  DO4(A,B,C,D,0,6,0xf4292244);
+  DO4(D,A,B,C,7,10,0x432aff97);
+  DO4(C,D,A,B,14,15,0xab9423a7);
+  DO4(B,C,D,A,5,21,0xfc93a039);
+
+  DO4(A,B,C,D,12,6,0x655b59c3);
+  DO4(D,A,B,C,3,10,0x8f0ccc92);
+  DO4(C,D,A,B,10,15,0xffeff47d);
+  DO4(B,C,D,A,1,21,0x85845dd1);
+
+  DO4(A,B,C,D,8,6,0x6fa87e4f);
+  DO4(D,A,B,C,15,10,0xfe2ce6e0);
+  DO4(C,D,A,B,6,15,0xa3014314);
+  DO4(B,C,D,A,13,21,0x4e0811a1);
+
+  DO4(A,B,C,D,4,6,0xf7537e82);
+  DO4(D,A,B,C,11,10,0xbd3af235);
+  DO4(C,D,A,B,2,15,0x2ad7d2bb);
+  DO4(B,C,D,A,9,21,0xeb86d391);
+
+  A += AA;
+  B += BB;
+  C += CC;
+  D += DD;
+}
+
+/*
+ * From `Performance analysis of MD5' by Joseph D. Touch <touch@isi.edu>
+ */
+
+#if defined(WORDS_BIGENDIAN)
+static inline u_int32_t
+swap_u_int32_t (u_int32_t t)
+{
+  u_int32_t temp1, temp2;
+
+  temp1   = cshift(t, 16);
+  temp2   = temp1 >> 8;
+  temp1  &= 0x00ff00ff;
+  temp2  &= 0x00ff00ff;
+  temp1 <<= 8;
+  return temp1 | temp2;
+}
+#endif
+
+struct x32{
+  unsigned int a:32;
+  unsigned int b:32;
+};
+
+void
+MD5_Update (struct md5 *m, const void *v, size_t len)
+{
+  const unsigned char *p = v;
+  size_t old_sz = m->sz[0];
+  size_t offset;
+
+  m->sz[0] += len * 8;
+  if (m->sz[0] < old_sz)
+      ++m->sz[1];
+  offset = (old_sz / 8)  % 64;
+  while(len > 0){
+    size_t l = min(len, 64 - offset);
+    memcpy(m->save + offset, p, l);
+    offset += l;
+    p += l;
+    len -= l;
+    if(offset == 64){
+#if defined(WORDS_BIGENDIAN)
+      int i;
+      u_int32_t current[16];
+      struct x32 *u = (struct x32*)m->save;
+      for(i = 0; i < 8; i++){
+	current[2*i+0] = swap_u_int32_t(u[i].a);
+	current[2*i+1] = swap_u_int32_t(u[i].b);
+      }
+      calc(m, current);
+#else
+      calc(m, (u_int32_t*)m->save);
+#endif
+      offset = 0;
+    }
+  }
+}
+
+void
+MD5_Final (void *res, struct md5 *m)
+{
+  static unsigned char zeros[72];
+  unsigned offset = (m->sz[0] / 8) % 64;
+  unsigned int dstart = (120 - offset - 1) % 64 + 1;
+
+  *zeros = 0x80;
+  memset (zeros + 1, 0, sizeof(zeros) - 1);
+  zeros[dstart+0] = (m->sz[0] >> 0) & 0xff;
+  zeros[dstart+1] = (m->sz[0] >> 8) & 0xff;
+  zeros[dstart+2] = (m->sz[0] >> 16) & 0xff;
+  zeros[dstart+3] = (m->sz[0] >> 24) & 0xff;
+  zeros[dstart+4] = (m->sz[1] >> 0) & 0xff;
+  zeros[dstart+5] = (m->sz[1] >> 8) & 0xff;
+  zeros[dstart+6] = (m->sz[1] >> 16) & 0xff;
+  zeros[dstart+7] = (m->sz[1] >> 24) & 0xff;
+  MD5_Update (m, zeros, dstart + 8);
+  {
+      int i;
+      unsigned char *r = (unsigned char *)res;
+
+      for (i = 0; i < 4; ++i) {
+	  r[4*i]   = m->counter[i] & 0xFF;
+	  r[4*i+1] = (m->counter[i] >> 8) & 0xFF;
+	  r[4*i+2] = (m->counter[i] >> 16) & 0xFF;
+	  r[4*i+3] = (m->counter[i] >> 24) & 0xFF;
+      }
+  }
+#if 0
+  {
+    int i;
+    u_int32_t *r = (u_int32_t *)res;
+
+    for (i = 0; i < 4; ++i)
+      r[i] = swap_u_int32_t (m->counter[i]);
+  }
+#endif
+}
diff --git a/md5.h b/md5.h
new file mode 100644
index 0000000..edcbb57
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Heimdal: md5.h,v 1.8 2001/01/29 02:08:57 assar Exp $
+   $NetBSD: md5.h,v 1.1.1.4 2002/09/12 12:41:42 joda Exp $ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct md5 {
+  unsigned int sz[2];
+  u_int32_t counter[4];
+  unsigned char save[64];
+};
+
+typedef struct md5 MD5_CTX;
+
+void MD5_Init (struct md5 *m);
+void MD5_Update (struct md5 *m, const void *p, size_t len);
+void MD5_Final (void *res, struct md5 *m); /* u_int32_t res[4] */
+
+#ifdef __cplusplus
+}
+#endif