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