Code drop from //branches/cupcake/...@124589
diff --git a/mountd/ASEC.c b/mountd/ASEC.c
new file mode 100644
index 0000000..3d8e50e
--- /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 0000000..c87b288
--- /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 87bcef3..bc128ac 100644
--- a/mountd/Android.mk
+++ b/mountd/Android.mk
@@ -6,7 +6,8 @@
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 bfe8ad1..0aac871 100644
--- a/mountd/AutoMount.c
+++ b/mountd/AutoMount.c
@@ -76,17 +76,20 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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,24 @@
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;
@@ -267,32 +354,39 @@
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
@@ -408,7 +502,7 @@
sync();
DropSystemCaches();
- if (DoUnmountDevice(mp->mountPoint) == 0)
+ if (DoUnmountDevice(mp) == 0)
{
SetState(mp, kUnmounted);
if (retryState == kUnmountingForUms)
@@ -502,6 +596,8 @@
{
MountPoint* mp = sMountPointList;
+ LOG_MOUNT("HandleMediaInserted(%s):\n", device);
+
while (mp)
{
// see if the device matches mount point's block device
@@ -573,7 +669,7 @@
}
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
@@ -594,8 +690,25 @@
// 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));
+
}
}
}
@@ -676,6 +789,8 @@
int id;
struct sigaction actions;
+ gExcludedPids[1] = getpid();
+
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
@@ -829,7 +944,9 @@
void MountMedia(const char* mountPoint)
{
MountPoint* mp = sMountPointList;
-
+
+ LOG_MOUNT("MountMedia(%s)\n", mountPoint);
+
pthread_mutex_lock(&sMutex);
while (mp)
{
@@ -883,27 +1000,48 @@
*
***********************************************/
-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;
@@ -916,6 +1054,8 @@
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 3ce7aa8..e377774 100644
--- a/mountd/ProcessKiller.c
+++ b/mountd/ProcessKiller.c
@@ -177,7 +177,7 @@
}
// 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 @@
|| 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 14b3830..64459bd 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 @@
// 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 @@
{
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 @@
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 @@
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 0000000..ee5c350
--- /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 fb54fe6..27ec8de 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 @@
{
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 @@
// 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 746a414..9b62484 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 @@
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 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 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 @@
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__