diff options
Diffstat (limited to 'mountd')
| -rw-r--r-- | mountd/ASEC.c | 770 | ||||
| -rw-r--r-- | mountd/ASEC.h | 66 | ||||
| -rw-r--r-- | mountd/Android.mk | 3 | ||||
| -rw-r--r-- | mountd/AutoMount.c | 213 | ||||
| -rw-r--r-- | mountd/ProcessKiller.c | 19 | ||||
| -rw-r--r-- | mountd/Server.c | 81 | ||||
| -rw-r--r-- | mountd/dm-ioctl.h | 304 | ||||
| -rw-r--r-- | mountd/mountd.c | 76 | ||||
| -rw-r--r-- | mountd/mountd.h | 37 |
9 files changed, 1522 insertions, 47 deletions
diff --git a/mountd/ASEC.c b/mountd/ASEC.c new file mode 100644 index 00000000..3d8e50e0 --- /dev/null +++ b/mountd/ASEC.c @@ -0,0 +1,770 @@ +/* + * 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. + */ + +/* +** Android Secure External Cache +*/ + +#include "mountd.h" + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <poll.h> +#include <errno.h> + +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <linux/loop.h> + +#include <cutils/properties.h> +#include <cutils/misc.h> + +#include "ASEC.h" +#include "dm-ioctl.h" + +extern int init_module(void *, unsigned long, const char *); +extern int delete_module(const char *, unsigned int); + +struct asec_context +{ + char *name; // Device mapper volume name + char *srcPath; // Path to the source (original) mount + char *backingFile; // Name of the image file + unsigned int sectors; // Number of sectors + char *dstPath; // Destination mount point + char *crypt; // Crypt options + + boolean needs_format; + boolean started; + int cacheFd; + int lo_num; + int dm_num; + unsigned char key[16]; +}; + +static const char *MODULES[] = { "dm_mod", "crypto", "crypto_algapi", "crypto_blkcipher", + "cryptomgr", "dm_crypt", "jbd", + "twofish_common", "twofish", "cbc", + "mbcache", "ext3", + NULL }; +static const char KEY_PATH[] = "/data/system/asec.key"; +static const char MODULE_PATH[] = "/system/lib/modules"; +static const char MKE2FS_PATH[] = "/system/bin/mke2fs"; +static const char E2FSCK_PATH[] = "/system/bin/e2fsck"; + +boolean AsecIsStarted(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + return ctx->started; +} + +const char *AsecMountPoint(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + return ctx->dstPath; +} + +static boolean AsecIsEnabled() +{ + char value[PROPERTY_VALUE_MAX]; + int enabled; + + property_get(ASEC_ENABLED, value, "0"); + + if (atoi(value) == 1) + return true; + return false; +} + +void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile, + const char *Size, const char *DstPath, const char *Crypt) +{ + struct asec_context *ctx; + + LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n", + Name, SrcPath, BackingFile, Size, DstPath, Crypt); + + if (!AsecIsEnabled()) { + LOG_ERROR("AsecInit(): Disabled\n"); + return NULL; + } + + if (!Name || !SrcPath || !BackingFile || !Size || !DstPath || !Crypt) { + LOG_ERROR("AsecInit(): Invalid arguments\n"); + return NULL; + } + + if (!(ctx = malloc(sizeof(struct asec_context)))) { + LOG_ERROR("AsecInit(): Out of memory\n"); + return NULL; + } + + memset(ctx, 0, sizeof(struct asec_context)); + ctx->name = strdup(Name); + ctx->srcPath = strdup(SrcPath); + ctx->backingFile = strdup(BackingFile); + ctx->sectors = atoi(Size); + ctx->dstPath = strdup(DstPath); + ctx->crypt = strdup(Crypt); + return ctx; +} + +void AsecDeinit(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + + free(ctx->name); + free(ctx->srcPath); + free(ctx->backingFile); + free(ctx->dstPath); + free(ctx->crypt); + + free(ctx); +} + +static int AsecLoadModules() +{ + int i; + + for (i = 0; MODULES[i] != NULL; i++) { + const char *moduleName = MODULES[i]; + char moduleFile[255]; + int rc = 0; + void *module; + unsigned int size; + + sprintf(moduleFile, "%s/%s.ko", MODULE_PATH, moduleName); + module = load_file(moduleFile, &size); + if (!module) { + LOG_ERROR("Failed to load module %s\n", moduleFile); + return -1; + } + + rc = init_module(module, size, ""); + free(module); + if (rc && errno != EEXIST) { + LOG_ERROR("Failed to init module %s (%d)\n", moduleFile, errno); + return -errno; + } + } + return 0; +} + +static int AsecUnloadModules() +{ + int i, j, rc; + + for (i = 0; MODULES[i] != NULL; i++); + + for (j = (i - 1); j >= 0; j--) { + const char *moduleName = MODULES[j]; + int maxretry = 10; + while(maxretry-- > 0) { + rc = delete_module(moduleName, O_NONBLOCK | O_EXCL); + if (rc < 0 && errno == EAGAIN) + usleep(500000); + else + break; + } + if (rc != 0) { + LOG_ERROR("Failed to unload module %s\n", moduleName); + return -errno; + } + } + return 0; +} + +static int AsecGenerateKey(struct asec_context *ctx) +{ + LOG_ASEC("AsecGenerateKey():\n"); + + memset((void *) ctx->key, 0x69, sizeof(ctx->key)); + return 0; +} + +static int AsecLoadGenerateKey(struct asec_context *ctx) +{ + int fd; + int rc = 0; + + if ((fd = open(KEY_PATH, O_RDWR | O_CREAT, 0600)) < 0) { + LOG_ERROR("Error opening / creating keyfile (%d)\n", errno); + return -errno; + } + + if (read(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) { + LOG_ASEC("Generating key\n"); + if ((rc = AsecGenerateKey(ctx)) < 0) { + LOG_ERROR("Error generating key (%d)\n", rc); + goto out; + } + if (write(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) { + LOG_ERROR("Error writing keyfile (%d)\n", errno); + rc = -1; + goto out; + } + } + + out: + close (fd); + return rc; +} + +static int AsecFormatFilesystem(struct asec_context *ctx) +{ + char cmdline[255]; + int rc; + + sprintf(cmdline, + "%s -b 4096 -m 1 -j -L \"%s\" /dev/block/dm-%d", + MKE2FS_PATH, ctx->name, ctx->dm_num); + + LOG_ASEC("Formatting filesystem (%s)\n", cmdline); + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing format command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (!rc) { + LOG_ASEC("Format completed\n"); + } else { + LOG_ASEC("Format failed (%d)\n", rc); + } + + return rc; +} + +static int AsecCheckFilesystem(struct asec_context *ctx) +{ + char cmdline[255]; + int rc; + + sprintf(cmdline, "%s -p /dev/block/dm-%d", E2FSCK_PATH, ctx->dm_num); + + LOG_ASEC("Checking filesystem (%s)\n", cmdline); + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing check command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (rc == 0) { + LOG_ASEC("ASEC volume '%s' had no errors\n", ctx->name); + } else if (rc == 1) { + LOG_ASEC("ASEC volume '%s' had corrected errors\n", ctx->name); + rc = 0; + } else if (rc == 2) { + LOG_ERROR("ASEC volume '%s' had corrected errors (system should be rebooted)\n", ctx->name); + } else if (rc == 4) { + LOG_ERROR("ASEC volume '%s' had uncorrectable errors\n", ctx->name); + } else if (rc == 8) { + LOG_ERROR("Operational error while checking volume '%s'\n", ctx->name); + } else { + LOG_ERROR("Unknown e2fsck exit code (%d)\n", rc); + } + return rc; +} + +static int AsecOpenCreateCache(struct asec_context *ctx) +{ + char filepath[255]; + + sprintf(filepath, "%s/%s", ctx->srcPath, ctx->backingFile); + + if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) { + if (errno == ENOENT) { + int rc = 0; + + LOG_ASEC("Creating cache file (%u sectors)\n", ctx->sectors); + if ((ctx->cacheFd = creat(filepath, 0600)) < 0) { + LOG_ERROR("Error creating cache (%d)\n", errno); + return -errno; + } + if (ftruncate(ctx->cacheFd, ctx->sectors * 512) < 0) { + LOG_ERROR("Error truncating cache (%d)\n", errno); + close(ctx->cacheFd); + unlink(filepath); + return -errno; + } + LOG_ASEC("Cache created (%u sectors) \n", ctx->sectors); + close(ctx->cacheFd); // creat() is WRONLY + + if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) { + LOG_ERROR("Error opening cache file (%d)\n", errno); + close(ctx->cacheFd); + unlink(filepath); + return -errno; + } + + ctx->needs_format = 1; + } else + return -errno; + } else { + struct stat stat_buf; + + if (fstat(ctx->cacheFd, &stat_buf) < 0) { + LOG_ERROR("Failed to fstat cache (%d)\n", errno); + close(ctx->cacheFd); + return -errno; + } + if (stat_buf.st_size != ctx->sectors * 512) { + LOG_ERROR("Cache size %lld != configured size %u\n", + stat_buf.st_size, ctx->sectors * 512); + } + + // XXX: Verify volume label matches ctx->name + } + + return 0; +} + +static void AsecCloseCache(struct asec_context *ctx) +{ + close(ctx->cacheFd); +} + +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +static struct dm_ioctl *_dm_ioctl_setup(struct asec_context *ctx, int flags) +{ + void *buffer; + void *p; + const size_t min_size = 16 * 1024; + size_t len = sizeof(struct dm_ioctl); + struct dm_ioctl *io; + struct dm_target_spec *tgt; + int i; + char params[1024]; + char key[80]; + + key[0] = '\0'; + + for (i = 0; i < (int) sizeof(ctx->key); i++) { + char tmp[8]; + + sprintf(tmp, "%02x", ctx->key[i]); + strcat(key, tmp); + } + + // XXX: Handle ctx->crypt + sprintf(params, "twofish %s 0 /dev/block/loop%d 0", key, ctx->lo_num); + + if (len < min_size) + len = min_size; + + if (!(buffer = malloc(len))) { + LOG_ERROR("Unable to allocate memory\n"); + return NULL; + } + + memset(buffer, 0, len); + io = buffer; + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + io->data_size = len; + io->data_start = sizeof(struct dm_ioctl); + + io->flags = flags; + io->dev = 0; + + io->target_count = 1; + io->event_nr = 1; + strncpy(io->name, ctx->name, sizeof(io->name)); + + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = ctx->sectors; + strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type)); + + p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + strcpy((char *) p, params); + p+= strlen(params) + 1; + + p = _align(p, 8); + tgt->next = p - buffer; + + return io; +} + +static int FindNextAvailableDm() +{ + int i; + + for (i = 0; i < 8; i++) { + char path[255]; + sprintf(path, "/dev/block/dm-%d", i); + if ((access(path, F_OK) < 0) && (errno == ENOENT)) + return i; + } + + LOG_ERROR("Out of device mapper numbers\n"); + return -1; +} + +static int AsecCreateDeviceMapping(struct asec_context *ctx) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + ctx->dm_num = FindNextAvailableDm(); + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOG_ERROR("Error opening device mapper (%d)\n", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(ctx, 0))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + close(dmFd); + return -ENOMEM; + } + + if ((rc = ioctl(dmFd, DM_DEV_CREATE, io)) < 0) { + LOG_ERROR("device-mapper create ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(ctx, DM_STATUS_TABLE_FLAG))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_TABLE_LOAD, io)) < 0) { + LOG_ERROR("device-mapper load ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + + free(io); + + if (!(io = _dm_ioctl_setup(ctx, 0))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_SUSPEND, io)) < 0) { + LOG_ERROR("device-mapper resume ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int AsecDestroyDeviceMapping(struct asec_context *ctx) +{ + struct dm_ioctl *io; + int dmFd; + int rc = 0; + + if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) { + LOG_ERROR("Error opening device mapper (%d)\n", errno); + return -errno; + } + + if (!(io = _dm_ioctl_setup(ctx, DM_PERSISTENT_DEV_FLAG))) { + LOG_ERROR("Unable to setup ioctl (out of memory)\n"); + rc = -ENOMEM; + goto out_nofree; + } + + if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) { + LOG_ERROR("device-mapper remove ioctl failed (%d)\n", errno); + rc = -errno; + goto out_free; + } + +out_free: + free (io); +out_nofree: + close (dmFd); + return rc; +} + +static int AsecMountCache(struct asec_context *ctx) +{ + int flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME; + char devname[255]; + + if (access(ctx->dstPath, R_OK)) { + LOG_ERROR("Destination mount point '%s' unavailable (%d)\n", ctx->dstPath, errno); + return -errno; + } + + sprintf(devname, "/dev/block/dm-%d", ctx->dm_num); + + if (mount(devname, ctx->dstPath, "ext3", flags, NULL)) { + LOG_ERROR("ASEC mount failed (%d)\n", errno); + return -errno; + } + + return 0; +} + +static int AsecUnmountCache(struct asec_context *ctx) +{ + if (umount(ctx->dstPath)) { + if (errno == EBUSY) { + LOG_ASEC("ASEC volume '%s' still busy\n", ctx->name); + } else { + LOG_ERROR("ASEC umount failed (%d)\n", errno); + } + return -errno; + } + LOG_ASEC("ASEC volume '%s' unmounted\n", ctx->name); + return 0; +} + +static int FindNextAvailableLoop() +{ + int i; + + for (i = 0; i < MAX_LOOP; i++) { + struct loop_info info; + char devname[255]; + int fd; + + sprintf(devname, "/dev/block/loop%d", i); + + if ((fd = open(devname, O_RDONLY)) < 0) { + LOG_ERROR("Unable to open %s (%d)\n", devname, errno); + return -errno; + } + + if (ioctl(fd, LOOP_GET_STATUS, &info) < 0) { + close(fd); + + if (errno == ENXIO) + return i; + + LOG_ERROR("Unable to get loop status for %s (%d)\n", devname, errno); + return -errno; + } + close(fd); + } + return -ENXIO; +} + +static int AsecCreateLoop(struct asec_context *ctx) +{ + char devname[255]; + int device_fd; + int rc = 0; + + ctx->lo_num = FindNextAvailableLoop(); + if (ctx->lo_num < 0) { + LOG_ERROR("No loop devices available\n"); + return -ENXIO; + } + + sprintf(devname, "/dev/block/loop%d", ctx->lo_num); + device_fd = open(devname, O_RDWR); + if (device_fd < 0) { + LOG_ERROR("failed to open loop device (%d)\n", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_SET_FD, ctx->cacheFd) < 0) { + LOG_ERROR("loop_set_fd ioctl failed (%d)\n", errno); + rc = -errno; + } + close(device_fd); + return rc; +} + +static int AsecDestroyLoop(struct asec_context *ctx) +{ + char devname[255]; + int device_fd; + int rc = 0; + + sprintf(devname, "/dev/block/loop%d", ctx->lo_num); + device_fd = open(devname, O_RDONLY); + if (device_fd < 0) { + LOG_ERROR("Failed to open loop (%d)\n", errno); + return -errno; + } + + if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("Failed to destroy loop (%d)\n", errno); + rc = -errno; + } + + close(device_fd); + return rc; +} + +int AsecStart(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + char value[PROPERTY_VALUE_MAX]; + int rc = 0; + + if (!ctx) + return -EINVAL; + + if (ctx->started) + return -EBUSY; + + LOG_ASEC("AsecStart(%s):\n", ctx->name); + + NotifyAsecState(ASEC_BUSY, ctx->dstPath); + + if ((rc = AsecLoadModules()) < 0) { + LOG_ERROR("AsecStart: Failed to load kernel modules\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecLoadGenerateKey(ctx))) { + LOG_ERROR("AsecStart: Failed to load / generate key\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecOpenCreateCache(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to open / create cache\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecCreateLoop(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to create loop\n"); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_closecache; + } + + if ((rc = AsecCreateDeviceMapping(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to create devmapping (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroyloop; + } + + if (ctx->needs_format) { + if ((rc = AsecFormatFilesystem(ctx))) { + LOG_ERROR("AsecStart: Failed to format cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + ctx->needs_format = 0; + } else { + if ((rc = AsecCheckFilesystem(ctx))) { + LOG_ERROR("AsecStart: Failed to check filesystem (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + } + + if ((rc = AsecMountCache(ctx)) < 0) { + LOG_ERROR("AsecStart: Failed to mount cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + goto fail_destroydm; + } + + NotifyAsecState(ASEC_AVAILABLE, ctx->dstPath); + ctx->started = true; + + return rc; + + fail_destroydm: + AsecDestroyDeviceMapping(ctx); + fail_destroyloop: + AsecDestroyLoop(ctx); + fail_closecache: + AsecCloseCache(ctx); + return rc; +} + +int AsecStop(void *Handle) +{ + struct asec_context *ctx = (struct asec_context *) Handle; + int rc = 0; + + if (!ctx->started) + return -EINVAL; + + LOG_ASEC("AsecStop(%s):\n", ctx->name); + + NotifyAsecState(ASEC_BUSY, ctx->dstPath); + + if ((rc = AsecUnmountCache(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to unmount cache (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecDestroyDeviceMapping(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to destroy devmapping (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + if ((rc = AsecDestroyLoop(ctx)) < 0) { + LOG_ERROR("AsecStop: Failed to destroy loop device (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + + AsecCloseCache(ctx); + + if ((rc = AsecUnloadModules()) < 0) { + if (rc == -EAGAIN) { + LOG_ASEC("AsecStop: Kernel modules still in use\n"); + } else { + LOG_ERROR("AsecStop: Failed to unload kernel modules (%d)\n", rc); + NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath); + return rc; + } + } + + ctx->started = false; + NotifyAsecState(ASEC_DISABLED, ctx->dstPath); + return rc; +} diff --git a/mountd/ASEC.h b/mountd/ASEC.h new file mode 100644 index 00000000..c87b2887 --- /dev/null +++ b/mountd/ASEC.h @@ -0,0 +1,66 @@ +#ifndef _ASEC_H +#define _ASEC_H + +#define ASEC_STORES_MAX 4 +#define MAX_LOOP 8 + +typedef enum AsecState { + // Feature disabled + ASEC_DISABLED, + + // Feature enabled and operational + ASEC_AVAILABLE, + + // Busy + ASEC_BUSY, + + // Internal Error + ASEC_FAILED_INTERR, + + // No media available + ASEC_FAILED_NOMEDIA, + + // Media is corrupt + ASEC_FAILED_BADMEDIA, + + // Key mismatch + ASEC_FAILED_BADKEY, +} AsecState; + +/* + * ASEC commands + */ +#define ASEC_CMD_SEND_STATUS "asec_send_status" +#define ASEC_CMD_ENABLE "asec_enable" +#define ASEC_CMD_DISABLE "asec_disable" + +/* + * ASEC events + */ + +// These events correspond to the states in the AsecState enum. +// A path to the ASEC mount point follows the colon +#define ASEC_EVENT_DISABLED "asec_disabled:" +#define ASEC_EVENT_AVAILABLE "asec_available:" +#define ASEC_EVENT_BUSY "asec_busy:" +#define ASEC_EVENT_FAILED_INTERR "asec_failed_interror:" +#define ASEC_EVENT_FAILED_NOMEDIA "asec_failed_nomedia" +#define ASEC_EVENT_FAILED_BADMEDIA "asec_failed_badmedia:" +#define ASEC_EVENT_FAILED_BADKEY "asec_failed_badkey:" + +/* + * System Properties + */ + +#define ASEC_ENABLED "asec.enabled" + +#define ASEC_STATUS "ro.asec.status" +#define ASEC_STATUS_DISABLED "disabled" +#define ASEC_STATUS_AVAILABLE "available" +#define ASEC_STATUS_BUSY "busy" +#define ASEC_STATUS_FAILED_INTERR "internal_error" +#define ASEC_STATUS_FAILED_NOMEDIA "no_media" +#define ASEC_STATUS_FAILED_BADMEDIA "bad_media" +#define ASEC_STATUS_FAILED_BADKEY "bad_key" + +#endif diff --git a/mountd/Android.mk b/mountd/Android.mk index 87bcef3e..bc128ac0 100644 --- a/mountd/Android.mk +++ b/mountd/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ AutoMount.c \ ProcessKiller.c \ Server.c \ - mountd.c + mountd.c \ + ASEC.c LOCAL_MODULE:= mountd diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c index 09e97590..0aac871d 100644 --- a/mountd/AutoMount.c +++ b/mountd/AutoMount.c @@ -76,17 +76,20 @@ typedef struct MountPoint { // mount point for device const char* mountPoint; + + // path to the UMS driver file for specifying the block device path + const char* driverStorePath; // true if device can be shared via // USB mass storage boolean enableUms; - + + // Array of ASEC handles + void *asecHandles[ASEC_STORES_MAX]; + // true if the device is being shared via USB mass storage boolean umsActive; - // logical unit number (for UMS) - int lun; - // current state of the mount point MountState state; @@ -100,11 +103,13 @@ typedef struct MountPoint { // list of our mount points (does not change after initialization) static MountPoint* sMountPointList = NULL; -static int sNextLun = 0; boolean gMassStorageEnabled = false; boolean gMassStorageConnected = false; static pthread_t sAutoMountThread = 0; +static pid_t gExcludedPids[2] = {-1, -1}; + +static const char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos"; // number of mount points that have timeouts pending static int sRetriesPending = 0; @@ -116,15 +121,18 @@ static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; // via USB mass storage. static void SetBackingStore(MountPoint* mp, boolean enable) { - char path[PATH_MAX]; int fd; + if (!mp->driverStorePath) { + LOG_ERROR("no driver_store_path specified in config file for %s", mp->device); + return; + } + LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false")); - snprintf(path, sizeof(path), "/sys/devices/platform/usb_mass_storage/lun%d/file", mp->lun); - fd = open(path, O_WRONLY); + fd = open(mp->driverStorePath, O_WRONLY); if (fd < 0) { - LOG_ERROR("could not open %s\n", path); + LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath); } else { @@ -192,6 +200,56 @@ static boolean IsLoopMounted(const char* path) return result; } +static int CheckFilesystem(const char *device) +{ + char cmdline[255]; + int rc; + + // XXX: SAN: Check for FAT signature + + int result = access(FSCK_MSDOS_PATH, X_OK); + if (result != 0) { + LOG_MOUNT("CheckFilesystem(%s): fsck_msdos not found (skipping checks)\n", device); + return 0; + } + + sprintf(cmdline, "%s -p %s", FSCK_MSDOS_PATH, device); + LOG_MOUNT("Checking filesystem (%s)\n", cmdline); + + // XXX: Notify framework we're disk checking + + // XXX: PROTECT FROM VIKING KILLER + if ((rc = system(cmdline)) < 0) { + LOG_ERROR("Error executing disk check command (%d)\n", errno); + return -errno; + } + + rc = WEXITSTATUS(rc); + + if (rc == 0) { + LOG_MOUNT("Filesystem check completed OK\n"); + return 0; + } else if (rc == 1) { + LOG_MOUNT("Filesystem check failed (invalid usage)\n"); + return -EINVAL; + } else if (rc == 2) { + LOG_MOUNT("Filesystem check failed (unresolved issues)\n"); + return -EIO; + } else if (rc == 4) { + LOG_MOUNT("Filesystem check failed (root changed)\n"); + return -EIO; + } else if (rc == 8) { + LOG_MOUNT("Filesystem check failed (general failure)\n"); + return -EIO; + } else if (rc == 12) { + LOG_MOUNT("Filesystem check failed (exit signaled)\n"); + return -EIO; + } else { + LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc); + return -EIO; + } +} + static int DoMountDevice(const char* device, const char* mountPoint) { LOG_MOUNT("mounting %s at %s\n", device, mountPoint); @@ -237,6 +295,17 @@ static int DoMountDevice(const char* device, const char* mountPoint) if (result != 0) return result; + if ((result = CheckFilesystem(device))) { + LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result); + // XXX: Notify framework - need a new SDCARD state for the following: + // - SD cards which are not present + // - SD cards with no partition table + // - SD cards with no filesystem + // - SD cards with bad filesystem + return result; + } + + // Extra safety measures: flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC; // Also, set fmask = 711 so that files cannot be marked executable, @@ -254,6 +323,27 @@ static int DoMountDevice(const char* device, const char* mountPoint) if (result == 0) { NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0); + + MountPoint* mp = sMountPointList; + while (mp) { + if (!strcmp(mountPoint, mp->mountPoint)) { + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] != NULL) { + int a_result; + if ((a_result = AsecStart(mp->asecHandles[i])) < 0) { + LOG_ERROR("ASEC start failure (%d)\n", a_result); + } + } + } + break; + } + mp = mp -> next; + } + } else if (errno == EBUSY) { + // ignore EBUSY, since it usually means the device is already mounted + result = 0; } else { #if CREATE_MOUNT_POINTS rmdir(mountPoint); @@ -264,32 +354,39 @@ static int DoMountDevice(const char* device, const char* mountPoint) return result; } -static int DoUnmountDevice(const char* mountPoint) +static int DoUnmountDevice(MountPoint *mp) { - boolean loop = IsLoopMounted(mountPoint); - int result = umount(mountPoint); + boolean loop = IsLoopMounted(mp->mountPoint); + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) + AsecStop(mp->asecHandles[i]); + } + + int result = umount(mp->mountPoint); LOG_MOUNT("umount returned %d errno: %d\n", result, errno); if (result == 0) { - if (loop) - { - // free the loop device - int loop_fd = open(LOOP_DEVICE, O_RDONLY); - if (loop_fd < -1) { - LOG_ERROR("open loop device failed\n"); - } - if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { - LOG_ERROR("ioctl LOOP_CLR_FD failed\n"); - } - - close(loop_fd); - } - #if CREATE_MOUNT_POINTS rmdir(mountPoint); #endif - NotifyMediaState(mountPoint, MEDIA_UNMOUNTED, false); + NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false); + } + + if (loop) + { + // free the loop device + int loop_fd = open(LOOP_DEVICE, O_RDONLY); + if (loop_fd < -1) { + LOG_ERROR("open loop device failed\n"); + } + if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { + LOG_ERROR("ioctl LOOP_CLR_FD failed\n"); + } + + close(loop_fd); } // ignore EINVAL and ENOENT, since it usually means the device is already unmounted @@ -405,7 +502,7 @@ static void RequestUnmount(MountPoint* mp, MountState retryState) sync(); DropSystemCaches(); - if (DoUnmountDevice(mp->mountPoint) == 0) + if (DoUnmountDevice(mp) == 0) { SetState(mp, kUnmounted); if (retryState == kUnmountingForUms) @@ -499,6 +596,8 @@ static void HandleMediaInserted(const char* device) { MountPoint* mp = sMountPointList; + LOG_MOUNT("HandleMediaInserted(%s):\n", device); + while (mp) { // see if the device matches mount point's block device @@ -570,7 +669,7 @@ static void HandleRetries() } else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms) { - if (DoUnmountDevice(mp->mountPoint) == 0) + if (DoUnmountDevice(mp) == 0) { // unmounting succeeded // start mass storage, if state is kUnmountingForUms @@ -591,8 +690,25 @@ static void HandleRetries() // send SIGKILL instead of SIGTERM if the first attempt did not succeed boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES); + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) { + LOG_MOUNT("Killing processes for ASEC path '%s'\n", + AsecMountPoint(mp->asecHandles[i])); + KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]), + sigkill, + gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t)); + + // Now that we've killed the processes, try to stop the volume again + AsecStop(mp->asecHandles[i]); + } + } + // unmounting the device is failing, so start killing processes - KillProcessesWithOpenFiles(mp->mountPoint, sigkill); + KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids, + sizeof(gExcludedPids) / sizeof(pid_t)); + } } } @@ -673,6 +789,8 @@ static void* AutoMountThread(void* arg) int id; struct sigaction actions; + gExcludedPids[1] = getpid(); + memset(&actions, 0, sizeof(actions)); sigemptyset(&actions.sa_mask); actions.sa_flags = 0; @@ -826,7 +944,9 @@ void EnableMassStorage(boolean enable) void MountMedia(const char* mountPoint) { MountPoint* mp = sMountPointList; - + + LOG_MOUNT("MountMedia(%s)\n", mountPoint); + pthread_mutex_lock(&sMutex); while (mp) { @@ -880,27 +1000,48 @@ boolean IsMassStorageConnected() * ***********************************************/ -void AddMountPoint(const char* device, const char* mountPoint, boolean enableUms) +void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms) { MountPoint* newMountPoint; - LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s\n", device, mountPoint); + LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath); // add a new MountPoint to the head of our linked list newMountPoint = (MountPoint *)malloc(sizeof(MountPoint)); newMountPoint->device = device; newMountPoint->mountPoint = mountPoint; + newMountPoint->driverStorePath = driverStorePath; newMountPoint->enableUms = enableUms; newMountPoint->umsActive = false; - if (enableUms) - newMountPoint->lun = sNextLun++; newMountPoint->state = kUnmounted; newMountPoint->retryCount = 0; // add to linked list newMountPoint->next = sMountPointList; sMountPointList = newMountPoint; + return newMountPoint; } +int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size, + const char *mount_point, const char *crypt) +{ + MountPoint *mp = (MountPoint *) Mp; + int i; + + for (i = 0; i < ASEC_STORES_MAX; i++) { + if (!mp->asecHandles[i]) + break; + } + + if (i == ASEC_STORES_MAX) { + LOG_ERROR("Maximum # of ASEC stores exceeded\n"); + return -EINVAL; + } + + if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt))) + return -1; + + return 0; +} static void MountDevices() { MountPoint* mp = sMountPointList; @@ -913,6 +1054,8 @@ static void MountDevices() void StartAutoMounter() { + gExcludedPids[0] = getpid(); + gMassStorageConnected = ReadMassStorageState(); LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n"); diff --git a/mountd/ProcessKiller.c b/mountd/ProcessKiller.c index 3ce7aa81..e3777744 100644 --- a/mountd/ProcessKiller.c +++ b/mountd/ProcessKiller.c @@ -177,7 +177,7 @@ static int get_pid(const char* s) } // hunt down and kill processes that have files open on the given mount point -void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill) +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded) { DIR* dir; struct dirent* de; @@ -200,8 +200,21 @@ void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill) || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path ) { - LOG_ERROR("Killing process %d\n", pid); - kill(pid, (sigkill ? SIGKILL : SIGTERM)); + int i; + boolean hit = false; + + for (i = 0; i < num_excluded; i++) { + if (pid == excluded[i]) { + LOG_ERROR("I just need a little more TIME captain!\n"); + hit = true; + break; + } + } + + if (!hit) { + LOG_ERROR("Killing process %d\n", pid); + kill(pid, (sigkill ? SIGKILL : SIGTERM)); + } } } diff --git a/mountd/Server.c b/mountd/Server.c index 14b38300..64459bd8 100644 --- a/mountd/Server.c +++ b/mountd/Server.c @@ -19,6 +19,7 @@ */ #include "mountd.h" +#include "ASEC.h" #include <cutils/properties.h> #include <cutils/sockets.h> @@ -43,6 +44,10 @@ static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER; // path for media that failed to mount before the runtime is connected static char* sDeferredUnmountableMediaPath = NULL; +// last asec msg before the runtime was connected +static char* sAsecDeferredMessage = NULL; +static char* sAsecDeferredArgument = NULL; + static int Write(const char* message) { int result = -1; @@ -107,6 +112,18 @@ static void DoCommand(const char* command) { const char* path = command + strlen(MOUNTD_EJECT_MEDIA); UnmountMedia(path); + } + else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) { + LOG_ASEC("Got ASEC_CMD_ENABLE\n"); + // XXX: SAN: Impliment + } + else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) { + LOG_ASEC("Got ASEC_CMD_DISABLE\n"); + // XXX: SAN: Impliment + } + else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) { + LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n"); + // XXX: SAN: Impliment } else LOGE("unknown command %s\n", command); @@ -145,6 +162,15 @@ int RunServer() sDeferredUnmountableMediaPath = NULL; } + if (sAsecDeferredMessage) { + + if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0) + LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n"); + free(sAsecDeferredMessage); + free(sAsecDeferredArgument); + sAsecDeferredMessage = sAsecDeferredArgument = NULL; + } + while (1) { char buffer[101]; @@ -187,6 +213,61 @@ void SendUnmountRequest(const char* path) Write2(MOUNTD_REQUEST_EJECT, path); } +void NotifyAsecState(AsecState state, const char *argument) +{ + const char *event = NULL; + const char *status = NULL; + boolean deferr = true;; + + switch (state) { + case ASEC_DISABLED: + event = ASEC_EVENT_DISABLED; + status = ASEC_STATUS_DISABLED; + break; + case ASEC_AVAILABLE: + event = ASEC_EVENT_AVAILABLE; + status = ASEC_STATUS_AVAILABLE; + break; + case ASEC_BUSY: + event = ASEC_EVENT_BUSY; + status = ASEC_STATUS_BUSY; + deferr = false; + break; + case ASEC_FAILED_INTERR: + event = ASEC_EVENT_FAILED_INTERR; + status = ASEC_STATUS_FAILED_INTERR; + break; + case ASEC_FAILED_NOMEDIA: + event = ASEC_EVENT_FAILED_NOMEDIA; + status = ASEC_STATUS_FAILED_NOMEDIA; + break; + case ASEC_FAILED_BADMEDIA: + event = ASEC_EVENT_FAILED_BADMEDIA; + status = ASEC_STATUS_FAILED_BADMEDIA; + break; + case ASEC_FAILED_BADKEY: + event = ASEC_EVENT_FAILED_BADKEY; + status = ASEC_STATUS_FAILED_BADKEY; + break; + default: + LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state); + return; + } + + property_set(ASEC_STATUS, status); + + int result = Write2(event, argument); + if ((result < 0) && deferr) { + if (sAsecDeferredMessage) + free(sAsecDeferredMessage); + sAsecDeferredMessage = strdup(event); + if (sAsecDeferredArgument) + free(sAsecDeferredArgument); + sAsecDeferredArgument = strdup(argument); + LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument); + } +} + void NotifyMediaState(const char* path, MediaState state, boolean readOnly) { const char* event = NULL; diff --git a/mountd/dm-ioctl.h b/mountd/dm-ioctl.h new file mode 100644 index 00000000..ee5c3508 --- /dev/null +++ b/mountd/dm-ioctl.h @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited. + * Copyright (C) 2004 - 2005 Red Hat, Inc. All rights reserved. + * + * This file is released under the LGPL. + */ + +#ifndef _LINUX_DM_IOCTL_V4_H +#define _LINUX_DM_IOCTL_V4_H + +#ifdef linux +# include <linux/types.h> +#endif + +#define DM_DIR "mapper" /* Slashes not supported */ +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 + +/* + * A traditional ioctl interface for the device mapper. + * + * Each device can have two tables associated with it, an + * 'active' table which is the one currently used by io passing + * through the device, and an 'inactive' one which is a table + * that is being prepared as a replacement for the 'active' one. + * + * DM_VERSION: + * Just get the version information for the ioctl interface. + * + * DM_REMOVE_ALL: + * Remove all dm devices, destroy all tables. Only really used + * for debug. + * + * DM_LIST_DEVICES: + * Get a list of all the dm device names. + * + * DM_DEV_CREATE: + * Create a new device, neither the 'active' or 'inactive' table + * slots will be filled. The device will be in suspended state + * after creation, however any io to the device will get errored + * since it will be out-of-bounds. + * + * DM_DEV_REMOVE: + * Remove a device, destroy any tables. + * + * DM_DEV_RENAME: + * Rename a device. + * + * DM_SUSPEND: + * This performs both suspend and resume, depending which flag is + * passed in. + * Suspend: This command will not return until all pending io to + * the device has completed. Further io will be deferred until + * the device is resumed. + * Resume: It is no longer an error to issue this command on an + * unsuspended device. If a table is present in the 'inactive' + * slot, it will be moved to the active slot, then the old table + * from the active slot will be _destroyed_. Finally the device + * is resumed. + * + * DM_DEV_STATUS: + * Retrieves the status for the table in the 'active' slot. + * + * DM_DEV_WAIT: + * Wait for a significant event to occur to the device. This + * could either be caused by an event triggered by one of the + * targets of the table in the 'active' slot, or a table change. + * + * DM_TABLE_LOAD: + * Load a table into the 'inactive' slot for the device. The + * device does _not_ need to be suspended prior to this command. + * + * DM_TABLE_CLEAR: + * Destroy any table in the 'inactive' slot (ie. abort). + * + * DM_TABLE_DEPS: + * Return a set of device dependencies for the 'active' table. + * + * DM_TABLE_STATUS: + * Return the targets status for the 'active' table. + * + * DM_TARGET_MSG: + * Pass a message string to the target at a specific offset of a device. + * + * DM_DEV_SET_GEOMETRY: + * Set the geometry of a device by passing in a string in this format: + * + * "cylinders heads sectors_per_track start_sector" + * + * Beware that CHS geometry is nearly obsolete and only provided + * for compatibility with dm devices that can be booted by a PC + * BIOS. See struct hd_geometry for range limits. Also note that + * the geometry is erased if the device size changes. + */ + +/* + * All ioctl arguments consist of a single chunk of memory, with + * this structure at the start. If a uuid is specified any + * lookup (eg. for a DM_INFO) will be done on that, *not* the + * name. + */ +struct dm_ioctl { + /* + * The version number is made up of three parts: + * major - no backward or forward compatibility, + * minor - only backwards compatible, + * patch - both backwards and forwards compatible. + * + * All clients of the ioctl interface should fill in the + * version number of the interface that they were + * compiled with. + * + * All recognised ioctl commands (ie. those that don't + * return -ENOTTY) fill out this field, even if the + * command failed. + */ + uint32_t version[3]; /* in/out */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + uint32_t target_count; /* in/out */ + int32_t open_count; /* out */ + uint32_t flags; /* in/out */ + uint32_t event_nr; /* in/out */ + uint32_t padding; + + uint64_t dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ + char data[7]; /* padding or data */ +}; + +/* + * Used to specify tables. These structures appear after the + * dm_ioctl. + */ +struct dm_target_spec { + uint64_t sector_start; + uint64_t length; + int32_t status; /* used when reading from kernel only */ + + /* + * Location of the next dm_target_spec. + * - When specifying targets on a DM_TABLE_LOAD command, this value is + * the number of bytes from the start of the "current" dm_target_spec + * to the start of the "next" dm_target_spec. + * - When retrieving targets on a DM_TABLE_STATUS command, this value + * is the number of bytes from the start of the first dm_target_spec + * (that follows the dm_ioctl struct) to the start of the "next" + * dm_target_spec. + */ + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ +}; + +/* + * Used to retrieve the target dependencies. + */ +struct dm_target_deps { + uint32_t count; /* Array size */ + uint32_t padding; /* unused */ + uint64_t dev[0]; /* out */ +}; + +/* + * Used to get a list of all dm devices. + */ +struct dm_name_list { + uint64_t dev; + uint32_t next; /* offset to the next record from + the _start_ of this */ + char name[0]; +}; + +/* + * Used to retrieve the target versions + */ +struct dm_target_versions { + uint32_t next; + uint32_t version[3]; + + char name[0]; +}; + +/* + * Used to pass message to a target + */ +struct dm_target_msg { + uint64_t sector; /* Device sector */ + + char message[0]; +}; + +/* + * If you change this make sure you make the corresponding change + * to dm-ioctl.c:lookup_ioctl() + */ +enum { + /* Top level cmds */ + DM_VERSION_CMD = 0, + DM_REMOVE_ALL_CMD, + DM_LIST_DEVICES_CMD, + + /* device level cmds */ + DM_DEV_CREATE_CMD, + DM_DEV_REMOVE_CMD, + DM_DEV_RENAME_CMD, + DM_DEV_SUSPEND_CMD, + DM_DEV_STATUS_CMD, + DM_DEV_WAIT_CMD, + + /* Table level cmds */ + DM_TABLE_LOAD_CMD, + DM_TABLE_CLEAR_CMD, + DM_TABLE_DEPS_CMD, + DM_TABLE_STATUS_CMD, + + /* Added later */ + DM_LIST_VERSIONS_CMD, + DM_TARGET_MSG_CMD, + DM_DEV_SET_GEOMETRY_CMD +}; + +#define DM_IOCTL 0xfd + +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) +#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl) + +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) +#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl) + +#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl) +#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl) +#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl) +#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl) + +#define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl) + +#define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl) +#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) + +#define DM_VERSION_MAJOR 4 +#define DM_VERSION_MINOR 13 +#define DM_VERSION_PATCHLEVEL 0 +#define DM_VERSION_EXTRA "-ioctl (2007-10-18)" + +/* Status bits */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +/* + * This flag is now ignored. + */ +#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ + +/* + * Set this to avoid attempting to freeze any filesystem when suspending. + */ +#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */ + +/* + * Set this to suspend without flushing queued ios. + */ +#define DM_NOFLUSH_FLAG (1 << 11) /* In */ + +#endif /* _LINUX_DM_IOCTL_H */ diff --git a/mountd/mountd.c b/mountd/mountd.c index fb54fe68..27ec8de9 100644 --- a/mountd/mountd.c +++ b/mountd/mountd.c @@ -39,6 +39,54 @@ FILE* logFile; #endif +struct asec_cfg { + const char *name; + const char *backing_file; + const char *size; + const char *mount_point; + const char *crypt; +}; + +static int ProcessAsecData(cnode *node, struct asec_cfg *stores, int idx) +{ + cnode *child = node->first_child; + const char *name = NULL; + const char *file = NULL; + const char *size = NULL; + const char *mp = NULL; + const char *crypt = NULL; + + LOG_ASEC("ProcessAsecData(%s, %p, %d)\n", node->name, stores, idx); + + while (child) { + if (!strcmp(child->name, "name")) + name = child->value; + else if (!strcmp(child->name, "backing_file")) + file = child->value; + else if (!strcmp(child->name, "size")) + size = child->value; + else if (!strcmp(child->name, "mount_point")) + mp = child->value; + else if (!strcmp(child->name, "crypt")) + crypt = child->value; + child = child->next; + } + + if (!name || !file || !size || !mp || !crypt) { + LOG_ERROR("Missing required token from config. Skipping ASEC volume\n"); + return -1; + } else if (idx == ASEC_STORES_MAX) { + LOG_ERROR("Maximum # of ASEC stores already defined\n"); + return -1; + } + + stores[idx].name = name; + stores[idx].backing_file = file; + stores[idx].size = size; + stores[idx].mount_point = mp; + stores[idx].crypt = crypt; + return ++idx; +} static void ReadConfigFile(const char* path) { @@ -54,18 +102,31 @@ static void ReadConfigFile(const char* path) { const char* block_device = NULL; const char* mount_point = NULL; + const char* driver_store_path = NULL; boolean enable_ums = false; cnode* child = node->first_child; - + struct asec_cfg asec_stores[ASEC_STORES_MAX]; + int asec_idx = 0; + + memset(asec_stores, 0, sizeof(asec_stores)); + while (child) { const char* name = child->name; const char* value = child->value; - - if (strcmp(name, "block_device") == 0) + + if (!strncmp(name, "asec_", 5)) { + int rc = ProcessAsecData(child, asec_stores, asec_idx); + if (rc < 0) { + LOG_ERROR("Error processing ASEC cfg data\n"); + } else + asec_idx = rc; + } else if (strcmp(name, "block_device") == 0) block_device = value; else if (strcmp(name, "mount_point") == 0) mount_point = value; + else if (strcmp(name, "driver_store_path") == 0) + driver_store_path = value; else if (strcmp(name, "enable_ums") == 0 && strcmp(value, "true") == 0) enable_ums = true; @@ -76,7 +137,14 @@ static void ReadConfigFile(const char* path) // mount point and removable fields are optional if (block_device && mount_point) { - AddMountPoint(block_device, mount_point, enable_ums); + void *mp = AddMountPoint(block_device, mount_point, driver_store_path, enable_ums); + int i; + + for (i = 0; i < asec_idx; i++) { + AddAsecToMountPoint(mp, asec_stores[i].name, asec_stores[i].backing_file, + asec_stores[i].size, asec_stores[i].mount_point, + asec_stores[i].crypt); + } } } diff --git a/mountd/mountd.h b/mountd/mountd.h index 746a4148..9b624847 100644 --- a/mountd/mountd.h +++ b/mountd/mountd.h @@ -20,21 +20,28 @@ #define LOG_TAG "mountd" #include "cutils/log.h" +#include "ASEC.h" + typedef int boolean; enum { false = 0, true = 1 }; +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) + // Set this for logging error messages #define ENABLE_LOG_ERROR // set this to log automounter events -//#define ENABLE_LOG_MOUNT +#define ENABLE_LOG_MOUNT // set this to log server events //#define ENABLE_LOG_SERVER +// set this to log ASEC events +#define ENABLE_LOG_ASEC + #ifdef ENABLE_LOG_ERROR #define LOG_ERROR(fmt, args...) \ { LOGE(fmt , ## args); } @@ -59,6 +66,14 @@ enum { do { } while (0) #endif /* ENABLE_LOG_SERVER */ +#ifdef ENABLE_LOG_ASEC +#define LOG_ASEC(fmt, args...) \ + { LOGD(fmt , ## args); } +#else +#define LOG_ASEC(fmt, args...) \ + do { } while (0) +#endif /* ENABLE_LOG_ASEC */ + typedef enum MediaState { // no media in SD card slot @@ -135,7 +150,11 @@ void UnmountMedia(const char* mountPoint); void EnableMassStorage(boolean enable); // call this before StartAutoMounter() to add a mount point to monitor -void AddMountPoint(const char* device, const char* mountPoint, boolean enableUms); +void *AddMountPoint(const char* device, const char* mountPoint, const char* driverStorePath, + boolean enableUms); + +int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, + const char *size, const char *mount_point, const char *crypt); // start automounter thread void StartAutoMounter(); @@ -144,9 +163,19 @@ void StartAutoMounter(); void NotifyExistingMounts(); +// ASEC.c + +void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile, + const char *Size, const char *DstPath, const char *Crypt); +int AsecStart(void *Handle); +int AsecStop(void *Handle); +void AsecDeinit(void *Handle); +boolean AsecIsStarted(void *Handle); +const char *AsecMountPoint(void *Handle); + // ProcessKiller.c -void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill); +void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, pid_t *excluded, int num_excluded); // Server.c @@ -155,5 +184,5 @@ int RunServer(); void SendMassStorageConnected(boolean connected); void SendUnmountRequest(const char* path); void NotifyMediaState(const char* path, MediaState state, boolean readOnly); - +void NotifyAsecState(AsecState state, const char *argument); #endif // MOUNTD_H__ |
