diff --git a/Android.mk b/Android.mk index 1243ad7..d90b399 100644 --- a/Android.mk +++ b/Android.mk @@ -23,7 +23,8 @@ LOCAL_SRC_FILES:= \ ProcessKiller.c \ geom_mbr_enc.c \ Fat.cpp \ - Loop.cpp + Loop.cpp \ + Devmapper.cpp LOCAL_MODULE:= vold diff --git a/Devmapper.cpp b/Devmapper.cpp new file mode 100644 index 0000000..2b7d9a0 --- /dev/null +++ b/Devmapper.cpp @@ -0,0 +1,210 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#define LOG_TAG "Vold" + +#include + +#include "Devmapper.h" + +void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize, + const char *name, unsigned flags) { + memset(io, 0, dataSize); + io->data_size = dataSize; + io->data_start = sizeof(struct dm_ioctl); + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + io->flags = flags; + strncpy(io->name, name, sizeof(io->name)); +} + +int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) { + char *buffer = (char *) malloc(4096); + if (!buffer) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -1; + } + + int fd; + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening devmapper (%s)", strerror(errno)); + free(buffer); + return -1; + } + + struct dm_ioctl *io = (struct dm_ioctl *) buffer; + + ioctlInit(io, 4096, name, 0); + if (ioctl(fd, DM_DEV_STATUS, io)) { + if (errno != ENODEV) { + LOGE("Error retrieving device status (%s)", strerror(errno)); + } + free(buffer); + close(fd); + return -1; + } + close(fd); + + unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); + free(buffer); + LOGD("Newly created devmapper instance minor = %d\n", minor); + snprintf(ubuffer, len, "/dev/block/dm-%u", minor); + return 0; +} + +int Devmapper::create(const char *name, const char *loopFile, const char *key, int sizeMb, + char *ubuffer, size_t len) { + char *buffer = (char *) malloc(4096); + if (!buffer) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -1; + } + + int fd; + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening devmapper (%s)", strerror(errno)); + free(buffer); + return -1; + } + + struct dm_ioctl *io = (struct dm_ioctl *) buffer; + + // Create the DM device + ioctlInit(io, 4096, name, 0); + + if (ioctl(fd, DM_DEV_CREATE, io)) { + LOGE("Error creating device mapping (%s)", strerror(errno)); + free(buffer); + close(fd); + return -1; + } + + // Set the legacy geometry + ioctlInit(io, 4096, name, 0); + + char *geoParams = buffer + sizeof(struct dm_ioctl); + // bps=512 spc=8 res=32 nft=2 sec=8190 mid=0xf0 spt=63 hds=64 hid=0 bspf=8 rdcl=2 infs=1 bkbs=2 + strcpy(geoParams, "0 64 63 0"); + geoParams += strlen(geoParams) + 1; + geoParams = (char *) _align(geoParams, 8); + if (ioctl(fd, DM_DEV_SET_GEOMETRY, io)) { + LOGE("Error setting device geometry (%s)", strerror(errno)); + free(buffer); + close(fd); + return -1; + } + + // Retrieve the device number we were allocated + ioctlInit(io, 4096, name, 0); + if (ioctl(fd, DM_DEV_STATUS, io)) { + LOGE("Error retrieving device status (%s)", strerror(errno)); + free(buffer); + close(fd); + return -1; + } + + unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); + LOGD("Newly created devmapper instance minor = %d\n", minor); + snprintf(ubuffer, len, "/dev/block/dm-%u", minor); + + // Load the table + struct dm_target_spec *tgt; + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + ioctlInit(io, 4096, name, DM_STATUS_TABLE_FLAG); + io->target_count = 1; + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = (sizeMb * (1024 * 1024)) / 512; + strcpy(tgt->target_type, "crypt"); + + char *cryptParams = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + sprintf(cryptParams, "twofish %s 0 %s 0", key, loopFile); + cryptParams += strlen(cryptParams) + 1; + cryptParams = (char *) _align(cryptParams, 8); + tgt->next = cryptParams - buffer; + + if (ioctl(fd, DM_TABLE_LOAD, io)) { + LOGE("Error loading mapping table (%s)", strerror(errno)); + free(buffer); + close(fd); + return -1; + } + + // Resume the new table + ioctlInit(io, 4096, name, 0); + + if (ioctl(fd, DM_DEV_SUSPEND, io)) { + LOGE("Error Resuming (%s)", strerror(errno)); + free(buffer); + close(fd); + return -1; + } + + free(buffer); + + return 0; +} + +int Devmapper::destroy(const char *name) { + char *buffer = (char *) malloc(4096); + if (!buffer) { + LOGE("Error allocating memory (%s)", strerror(errno)); + return -1; + } + + int fd; + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOGE("Error opening devmapper (%s)", strerror(errno)); + free(buffer); + return -1; + } + + struct dm_ioctl *io = (struct dm_ioctl *) buffer; + + // Create the DM device + ioctlInit(io, 4096, name, 0); + + if (ioctl(fd, DM_DEV_REMOVE, io)) { + LOGE("Error destroying device mapping (%s)", strerror(errno)); + free(buffer); + close(fd); + return -1; + } + + free(buffer); + close(fd); + return 0; +} + +void *Devmapper::_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + diff --git a/Devmapper.h b/Devmapper.h new file mode 100644 index 0000000..d6cdd05 --- /dev/null +++ b/Devmapper.h @@ -0,0 +1,35 @@ +/* + * 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 _DEVMAPPER_H +#define _DEVMAPPER_H + +#include +#include + +class Devmapper { +public: + static int create(const char *name, const char *loopFile, const char *key, int sizeMb, + char *buffer, size_t len); + static int destroy(const char *name); + static int lookupActive(const char *name, char *buffer, size_t len); +private: + static void *_align(void *ptr, unsigned int a); + static void ioctlInit(struct dm_ioctl *io, size_t data_size, + const char *name, unsigned flags); +}; + +#endif diff --git a/Loop.cpp b/Loop.cpp index 1b3879a..b8106ef 100644 --- a/Loop.cpp +++ b/Loop.cpp @@ -43,7 +43,9 @@ int Loop::lookupActive(const char *loopFile, char *buffer, size_t len) { sprintf(filename, "/dev/block/loop%d", i); if ((fd = open(filename, O_RDWR)) < 0) { - LOGE("Unable to open %s (%s)", filename, strerror(errno)); + if (errno != ENOENT) { + LOGE("Unable to open %s (%s)", filename, strerror(errno)); + } return -1; } @@ -88,7 +90,7 @@ int Loop::create(const char *loopFile, char *loopDeviceBuffer, size_t len) { * are created on-demand if needed. */ mode_t mode = 0660 | S_IFBLK; - dev_t dev = (7 << 8) | i; + unsigned int dev = (0xff & i) | ((i << 12) & 0xfff00000) | (7 << 8); if (mknod(filename, mode, dev) < 0) { if (errno != EEXIST) { LOGE("Error creating loop device node (%s)", strerror(errno)); @@ -189,7 +191,7 @@ int Loop::createImageFile(const char *file, size_t sizeMb) { return -1; } - if (ftruncate(fd, (sizeMb * (1024 * 1024))) < 0) { + if (ftruncate(fd, 1024 + (sizeMb * (1024 * 1024))) < 0) { LOGE("Error truncating imagefile (%s)", strerror(errno)); close(fd); return -1; diff --git a/Loop.h b/Loop.h index 1ade4f9..da91dc2 100644 --- a/Loop.h +++ b/Loop.h @@ -22,7 +22,7 @@ class Loop { public: - static const int LOOP_MAX = 255; + 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); diff --git a/VolumeManager.cpp b/VolumeManager.cpp index a462196..201ad98 100644 --- a/VolumeManager.cpp +++ b/VolumeManager.cpp @@ -36,6 +36,7 @@ #include "ResponseCode.h" #include "Loop.h" #include "Fat.h" +#include "Devmapper.h" extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int); @@ -204,10 +205,27 @@ int VolumeManager::createAsec(const char *id, int sizeMb, return -1; } - /* XXX: Start devmapper */ + char dmDevice[255]; + bool cleanupDm = false; - if (Fat::format(loopDevice)) { + if (strcmp(key, "none")) { + if (Devmapper::create(id, loopDevice, key, sizeMb, dmDevice, + sizeof(dmDevice))) { + LOGE("ASEC device mapping failed (%s)", strerror(errno)); + Loop::destroyByDevice(loopDevice); + unlink(asecFileName); + return -1; + } + cleanupDm = true; + } else { + strcpy(dmDevice, loopDevice); + } + + if (Fat::format(dmDevice)) { LOGE("ASEC FAT format failed (%s)", strerror(errno)); + if (cleanupDm) { + Devmapper::destroy(id); + } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; @@ -219,16 +237,22 @@ int VolumeManager::createAsec(const char *id, int sizeMb, if (mkdir(mountPoint, 0777)) { if (errno != EEXIST) { LOGE("Mountpoint creation failed (%s)", strerror(errno)); + if (cleanupDm) { + Devmapper::destroy(id); + } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } } - if (Fat::doMount(loopDevice, mountPoint, false, false, ownerUid, + if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid, 0, 0000, false)) { // 0, 0007, false)) { LOGE("ASEC FAT mount failed (%s)", strerror(errno)); + if (cleanupDm) { + Devmapper::destroy(id); + } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; @@ -261,7 +285,7 @@ int VolumeManager::finalizeAsec(const char *id) { return 0; } -int VolumeManager::destroyAsec(const char *id) { +int VolumeManager::unmountAsec(const char *id) { char asecFileName[255]; char mountPoint[255]; @@ -270,35 +294,57 @@ int VolumeManager::destroyAsec(const char *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)); + LOGE("Unmount request for ASEC %s when not mounted", id); + errno = EINVAL; + return -1; + } - if (i >= 5) { - KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1), - NULL, 0); - } - usleep(1000 * 250); + int i, rc; + for (i = 0; i < 10; i++) { + rc = umount(mountPoint); + if (!rc) { + break; } - if (rc) { - LOGE("Failed to unmount ASEC %s for destroy", id); - return -1; + if (rc && (errno == EINVAL || errno == ENOENT)) { + rc = 0; + break; } + LOGW("ASEC %s unmount attempt %d failed (%s)", + id, i +1, strerror(errno)); + + if (i >= 5) { + KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1), + NULL, 0); + } + usleep(1000 * 250); + } + + if (rc) { + LOGE("Failed to unmount ASEC %s", id); + return -1; + } + + if (Devmapper::destroy(id) && errno != ENXIO) { + LOGE("Failed to destroy devmapper instance (%s)", strerror(errno)); } char loopDevice[255]; if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) { Loop::destroyByDevice(loopDevice); } + 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 (unmountAsec(id)) + return -1; unlink(asecFileName); @@ -326,17 +372,66 @@ int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) { LOGE("ASEC loop device creation failed (%s)", strerror(errno)); return -1; } + LOGD("New loop device created at %s", loopDevice); + } else { + LOGD("Found active loopback for %s at %s", asecFileName, loopDevice); + } + + char dmDevice[255]; + bool cleanupDm = false; + if (strcmp(key, "none")) { + if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) { + unsigned int nr_sec = 0; + int fd; + + if ((fd = open(loopDevice, O_RDWR)) < 0) { + LOGE("Failed to open loopdevice (%s)", strerror(errno)); + Loop::destroyByDevice(loopDevice); + return -1; + } + + if (ioctl(fd, BLKGETSIZE, &nr_sec)) { + LOGE("Failed to get loop size (%s)", strerror(errno)); + Loop::destroyByDevice(loopDevice); + close(fd); + return -1; + } + close(fd); + if (Devmapper::create(id, loopDevice, key, + (nr_sec * 512) / (1024 * 1024), + dmDevice, sizeof(dmDevice))) { + LOGE("ASEC device mapping failed (%s)", strerror(errno)); + Loop::destroyByDevice(loopDevice); + return -1; + } + LOGD("New devmapper instance created at %s", dmDevice); + } else { + LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice); + } + cleanupDm = true; + } else { + strcpy(dmDevice, loopDevice); } if (mkdir(mountPoint, 0777)) { - LOGE("Mountpoint creation failed (%s)", strerror(errno)); - return -1; + if (errno != EEXIST) { + LOGE("Mountpoint creation failed (%s)", strerror(errno)); + if (cleanupDm) { + Devmapper::destroy(id); + } + Loop::destroyByDevice(loopDevice); + return -1; + } } - if (Fat::doMount(loopDevice, mountPoint, true, false, ownerUid, 0, + if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0, 0222, false)) { // 0227, false)) { LOGE("ASEC mount failed (%s)", strerror(errno)); + if (cleanupDm) { + Devmapper::destroy(id); + } + Loop::destroyByDevice(loopDevice); return -1; } diff --git a/VolumeManager.h b/VolumeManager.h index a0b63ac..80b4ccf 100644 --- a/VolumeManager.h +++ b/VolumeManager.h @@ -60,6 +60,7 @@ public: int finalizeAsec(const char *id); int destroyAsec(const char *id); int mountAsec(const char *id, const char *key, int ownerUid); + int unmountAsec(const char *id); int getAsecMountPath(const char *id, char *buffer, int maxlen); // XXX: This should be moved private once switch uevents are working