auto import from //depot/cupcake/@135843
diff --git a/mountd/ASEC.c b/mountd/ASEC.c
new file mode 100644
index 0000000..a6aab9c
--- /dev/null
+++ b/mountd/ASEC.c
@@ -0,0 +1,774 @@
+/*
+ * 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/dm-ioctl.h>
+#include <linux/loop.h>
+
+#include <cutils/properties.h>
+#include <cutils/misc.h>
+
+#include "ASEC.h"
+
+//#define MODULE_FAILURE_IS_FATAL
+
+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;
+
+ if (!AsecIsEnabled())
+ return NULL;
+
+ LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n",
+ Name, SrcPath, BackingFile, Size, DstPath, Crypt);
+
+ 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 (%d)\n", moduleFile, errno);
+ 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");
+#ifdef MODULE_FAILURE_IS_FATAL
+ NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+ return rc;
+#endif
+ }
+
+ 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);
+#ifdef MODULE_FAILURE_IS_FATAL
+ NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+ return rc;
+#endif
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..16532fa
--- /dev/null
+++ b/mountd/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AutoMount.c \
+ ProcessKiller.c \
+ Server.c \
+ mountd.c \
+ ASEC.c \
+ logwrapper.c
+
+LOCAL_MODULE:= mountd
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := -DCREATE_MOUNT_POINTS=0
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+# disabled - we are using vold now instead
+# include $(BUILD_EXECUTABLE)
diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c
new file mode 100644
index 0000000..12ad572
--- /dev/null
+++ b/mountd/AutoMount.c
@@ -0,0 +1,1062 @@
+/*
+ * 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.
+ */
+
+/*
+** mountd automount support
+*/
+
+#include "mountd.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/loop.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+
+#define DEVPATH "/dev/block/"
+#define DEVPATHLENGTH 11 // strlen(DEVPATH)
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+// timeout value for poll() when retries are pending
+#define POLL_TIMEOUT 1000
+
+#define MAX_MOUNT_RETRIES 3
+#define MAX_UNMOUNT_RETRIES 5
+
+typedef enum {
+ // device is unmounted
+ kUnmounted,
+
+ // attempting to mount device
+ kMounting,
+
+ // device is unmounted
+ kMounted,
+
+ // attempting to unmount device
+ // so the media can be removed
+ kUnmountingForEject,
+
+ // attempting to mount device
+ // so it can be shared via USB mass storage
+ kUnmountingForUms,
+} MountState;
+
+typedef struct MountPoint {
+ // block device to mount
+ const char* device;
+
+ // 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;
+
+ // current state of the mount point
+ MountState state;
+
+ // number of mount or unmount retries so far,
+ // when attempting to mount or unmount the device
+ int retryCount;
+
+ // next in sMountPointList linked list
+ struct MountPoint* next;
+} MountPoint;
+
+// list of our mount points (does not change after initialization)
+static MountPoint* sMountPointList = NULL;
+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/dosfsck";
+
+// number of mount points that have timeouts pending
+static int sRetriesPending = 0;
+
+// for synchronization between sAutoMountThread and the server thread
+static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
+
+// requests the USB mass_storage driver to begin or end sharing a block device
+// via USB mass storage.
+static void SetBackingStore(MountPoint* mp, boolean enable)
+{
+ 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"));
+ fd = open(mp->driverStorePath, O_WRONLY);
+ if (fd < 0)
+ {
+ LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath);
+ }
+ else
+ {
+ if (enable)
+ {
+ write(fd, mp->device, strlen(mp->device));
+ mp->umsActive = true;
+ }
+ else
+ {
+ char ch = 0;
+ write(fd, &ch, 1);
+ mp->umsActive = false;
+ }
+ close(fd);
+ }
+}
+
+static boolean ReadMassStorageState()
+{
+ FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r");
+ if (file)
+ {
+ char buffer[20];
+ fgets(buffer, sizeof(buffer), file);
+ fclose(file);
+ return (strncmp(buffer, "online", strlen("online")) == 0);
+ }
+ else
+ {
+ LOG_ERROR("could not read initial mass storage state\n");
+ return false;
+ }
+}
+
+static boolean IsLoopMounted(const char* path)
+{
+ FILE* f;
+ int count;
+ char device[256];
+ char mount_path[256];
+ char rest[256];
+ int result = 0;
+ int path_length = strlen(path);
+
+ f = fopen("/proc/mounts", "r");
+ if (!f) {
+ LOG_ERROR("could not open /proc/mounts\n");
+ return -1;
+ }
+
+ do {
+ count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
+ if (count == 3) {
+ if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0)
+ {
+ result = 1;
+ break;
+ }
+ }
+ } while (count == 3);
+
+ fclose(f);
+ LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result);
+ 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): %s not found (skipping checks)\n", FSCK_MSDOS_PATH, device);
+ return 0;
+ }
+
+ char *args[7];
+ args[0] = FSCK_MSDOS_PATH;
+ args[1] = "-v";
+ args[2] = "-V";
+ args[3] = "-w";
+ args[4] = "-p";
+ args[5] = device;
+ args[6] = NULL;
+
+ LOG_MOUNT("Checking filesystem on %s\n", device);
+ rc = logwrap(6, args);
+
+ // XXX: We need to be able to distinguish between a FS with an error
+ // and a block device which does not have a FAT fs at all on it
+ if (rc == 0) {
+ LOG_MOUNT("Filesystem check completed OK\n");
+ return 0;
+ } else if (rc == 1) {
+ LOG_MOUNT("Filesystem check failed (general failure)\n");
+ return -EINVAL;
+ } else if (rc == 2) {
+ LOG_MOUNT("Filesystem check failed (invalid usage)\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("Attempting mount of %s on %s\n", device, mountPoint);
+
+#if CREATE_MOUNT_POINTS
+ // make sure mount point exists
+ mkdir(mountPoint, 0000);
+#endif
+
+ int flags = 0;
+
+ if (device && strncmp(device, "/dev/", 5))
+ {
+ // mount with the loop driver if device does not start with "/dev/"
+ int file_fd, device_fd;
+
+ // FIXME - only one loop mount supported at a time
+ file_fd = open(device, O_RDWR);
+ if (file_fd < -1) {
+ LOG_ERROR("open backing file %s failed\n", device);
+ return 1;
+ }
+ device_fd = open(LOOP_DEVICE, O_RDWR);
+ if (device_fd < -1) {
+ LOG_ERROR("open %s failed", LOOP_DEVICE);
+ close(file_fd);
+ return 1;
+ }
+ if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
+ {
+ LOG_ERROR("ioctl LOOP_SET_FD failed\n");
+ close(file_fd);
+ close(device_fd);
+ return 1;
+ }
+
+ close(file_fd);
+ close(device_fd);
+ device = "/dev/block/loop0";
+ }
+
+ int result = access(device, R_OK);
+ if (result) {
+ LOG_ERROR("Unable to access '%s' (%d)\n", device, errno);
+ return -errno;
+ }
+
+#if 0
+ 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;
+ }
+#endif
+
+ // Extra safety measures:
+ flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+ // Also, set fmask = 711 so that files cannot be marked executable,
+ // and cannot by opened by uid 1000 (system). Similar, dmask = 700
+ // so that directories cannot be accessed by uid 1000.
+ result = mount(device, mountPoint, "vfat", flags,
+ "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+ if (result && errno == EROFS) {
+ LOG_ERROR("mount failed EROFS, try again read-only\n");
+ flags |= MS_RDONLY;
+ result = mount(device, mountPoint, "vfat", flags,
+ "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+ }
+
+ if (result == 0) {
+ LOG_MOUNT("Partition %s mounted on %s\n", device, mountPoint);
+ 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) {
+ LOG_MOUNT("Mount failed (already mounted)\n");
+ result = 0;
+ } else {
+#if CREATE_MOUNT_POINTS
+ rmdir(mountPoint);
+#endif
+ LOG_MOUNT("Unable to mount %s on %s\n", device, mountPoint);
+ }
+
+ return result;
+}
+
+static int DoUnmountDevice(MountPoint *mp)
+{
+ 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 CREATE_MOUNT_POINTS
+ rmdir(mountPoint);
+#endif
+ 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
+ if (result && (errno == EINVAL || errno == ENOENT))
+ result = 0;
+
+ return result;
+}
+
+static int MountPartition(const char* device, const char* mountPoint)
+{
+ char buf[100];
+ int i;
+
+ // attempt to mount subpartitions of the device
+ for (i = 1; i < 10; i++)
+ {
+ int rc;
+ snprintf(buf, sizeof(buf), "%sp%d", device, i);
+ rc = DoMountDevice(buf, mountPoint);
+ LOG_MOUNT("DoMountDevice(%s, %s) = %d\n", buf, mountPoint, rc);
+ if (rc == 0)
+ return 0;
+ }
+
+ return -1;
+}
+
+/*****************************************************
+ *
+ * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
+ *
+ *****************************************************/
+
+static void SetState(MountPoint* mp, MountState state)
+{
+ mp->state = state;
+}
+
+// Enter a state that requires retries and timeouts.
+static void SetRetries(MountPoint* mp, MountState state)
+{
+ SetState(mp, state);
+ mp->retryCount = 0;
+
+ sRetriesPending++;
+ // wake up the automounter thread if we are being called
+ // from somewhere else with no retries pending
+ if (sRetriesPending == 1 && sAutoMountThread != 0 &&
+ pthread_self() != sAutoMountThread)
+ pthread_kill(sAutoMountThread, SIGUSR1);
+}
+
+// Exit a state that requires retries and timeouts.
+static void ClearRetries(MountPoint* mp, MountState state)
+{
+ SetState(mp, state);
+ sRetriesPending--;
+}
+
+// attempt to mount the specified mount point.
+// set up retry/timeout if it does not succeed at first.
+static void RequestMount(MountPoint* mp)
+{
+ LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
+
+ if (mp->state != kMounted && mp->state != kMounting &&
+ access(mp->device, R_OK) == 0) {
+ // try raw device first
+ if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
+ MountPartition(mp->device, mp->mountPoint) == 0)
+ {
+ SetState(mp, kMounted);
+ }
+ else
+ {
+ SetState(mp, kMounting);
+ mp->retryCount = 0;
+ SetRetries(mp, kMounting);
+ }
+ }
+}
+
+// Force the kernel to drop all caches.
+static void DropSystemCaches(void)
+{
+ int fd;
+
+ LOG_MOUNT("Dropping system caches\n");
+ fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
+
+ if (fd > 0) {
+ char ch = 3;
+ int rc;
+
+ rc = write(fd, &ch, 1);
+ if (rc <= 0)
+ LOG_MOUNT("Error dropping caches (%d)\n", rc);
+ close(fd);
+ }
+}
+
+// attempt to unmount the specified mount point.
+// set up retry/timeout if it does not succeed at first.
+static void RequestUnmount(MountPoint* mp, MountState retryState)
+{
+ int result;
+
+ LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
+
+ if (mp->state == kMounted)
+ {
+ SendUnmountRequest(mp->mountPoint);
+
+ // do this in case the user pulls the SD card before we can successfully unmount
+ sync();
+ DropSystemCaches();
+
+ if (DoUnmountDevice(mp) == 0)
+ {
+ SetState(mp, kUnmounted);
+ if (retryState == kUnmountingForUms)
+ {
+ SetBackingStore(mp, true);
+ NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+ }
+ }
+ else
+ {
+ LOG_MOUNT("unmount failed, set retry\n");
+ SetRetries(mp, retryState);
+ }
+ }
+ else if (mp->state == kMounting)
+ {
+ SetState(mp, kUnmounted);
+ }
+}
+
+// returns true if the mount point should be shared via USB mass storage
+static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
+{
+ return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
+}
+
+// handles changes in gMassStorageEnabled and gMassStorageConnected
+static void MassStorageStateChanged()
+{
+ MountPoint* mp = sMountPointList;
+
+ boolean enable = (gMassStorageEnabled && gMassStorageConnected);
+ LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
+
+ while (mp)
+ {
+ if (mp->enableUms)
+ {
+ if (enable)
+ {
+ if (mp->state == kMounting)
+ SetState(mp, kUnmounted);
+ if (mp->state == kUnmounted)
+ {
+ SetBackingStore(mp, true);
+ NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+ }
+ else
+ {
+ LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
+ // need to successfully unmount first
+ RequestUnmount(mp, kUnmountingForUms);
+ }
+ } else if (mp->umsActive) {
+ SetBackingStore(mp, false);
+ if (mp->state == kUnmountingForUms)
+ {
+ ClearRetries(mp, kMounted);
+ NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
+ }
+ else if (mp->state == kUnmounted)
+ {
+ NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
+ RequestMount(mp);
+ }
+ }
+ }
+
+ mp = mp->next;
+ }
+}
+
+// called when USB mass storage connected state changes
+static void HandleMassStorageOnline(boolean connected)
+{
+ if (connected != gMassStorageConnected)
+ {
+ gMassStorageConnected = connected;
+ SendMassStorageConnected(connected);
+
+ // we automatically reset to mass storage off after USB is connected
+ if (!connected)
+ gMassStorageEnabled = false;
+
+ MassStorageStateChanged();
+ }
+}
+
+// called when a new block device has been created
+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
+ if (mp->state == kUnmounted &&
+ strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
+ {
+ if (MassStorageEnabledForMountPoint(mp))
+ {
+ SetBackingStore(mp, true);
+ NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+ }
+ else
+ RequestMount(mp);
+ }
+ mp = mp->next;
+ }
+}
+
+// called when a new block device has been deleted
+static void HandleMediaRemoved(const char* device)
+{
+ MountPoint* mp = sMountPointList;
+ while (mp)
+ {
+ if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
+ {
+ if (mp->enableUms)
+ SetBackingStore(mp, false);
+
+ if (mp->state == kMounted)
+ {
+ RequestUnmount(mp, kUnmountingForEject);
+ NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
+ }
+
+ NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
+ break;
+ }
+ mp = mp->next;
+ }
+}
+
+// Handle retrying to mount or unmount devices,
+// and handle timeout condition if we have tried too many times
+static void HandleRetries()
+{
+ MountPoint* mp = sMountPointList;
+
+ while (mp)
+ {
+ if (mp->state == kMounting)
+ {
+ if (MountPartition(mp->device, mp->mountPoint) == 0)
+ {
+ // mount succeeded - clear the retry for this mount point
+ ClearRetries(mp, kMounted);
+ }
+ else
+ {
+ mp->retryCount++;
+ if (mp->retryCount == MAX_MOUNT_RETRIES)
+ {
+ // we failed to mount the device too many times
+ ClearRetries(mp, kUnmounted);
+ // notify that we failed to mount
+ NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
+ }
+ }
+ }
+ else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
+ {
+ if (DoUnmountDevice(mp) == 0)
+ {
+ // unmounting succeeded
+ // start mass storage, if state is kUnmountingForUms
+ if (mp->state == kUnmountingForUms)
+ {
+ SetBackingStore(mp, true);
+ NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+ }
+ // clear the retry for this mount point
+ ClearRetries(mp, kUnmounted);
+ }
+ else
+ {
+ mp->retryCount++;
+ if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
+ {
+ // kill any processes that are preventing the device from unmounting
+ // 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, gExcludedPids,
+ sizeof(gExcludedPids) / sizeof(pid_t));
+
+ }
+ }
+ }
+
+ mp = mp->next;
+ }
+}
+
+/*****************************************************
+ *
+ * AUTO-MOUNTER THREAD
+ *
+ *****************************************************/
+
+static void sigusr1_handler(int signo)
+{
+ // don't need to do anything here
+}
+
+// create a socket for listening to inotify events
+int CreateINotifySocket()
+{
+ // initialize inotify
+ int fd = inotify_init();
+
+ if (fd < 0) {
+ LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
+
+ return fd;
+}
+
+
+// create a socket for listening to uevents
+int CreateUEventSocket()
+{
+ struct sockaddr_nl addr;
+ int sz = 64*1024;
+ int fd;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = getpid();
+ addr.nl_groups = 0xffffffff;
+
+ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if(fd < 0)
+ {
+ LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
+ return -1;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
+
+ if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/*
+ * Automounter main event thread.
+ * This thread listens for block devices being created and deleted via inotify,
+ * and listens for changes in the USB mass storage connected/disconnected via uevents from the
+ * power supply driver.
+ * This thread also handles retries and timeouts for requests to mount or unmount a device.
+ */
+static void* AutoMountThread(void* arg)
+{
+ int inotify_fd;
+ int uevent_fd;
+ int id;
+ struct sigaction actions;
+
+ gExcludedPids[1] = getpid();
+
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = sigusr1_handler;
+ sigaction(SIGUSR1, &actions, NULL);
+
+ // initialize inotify
+ inotify_fd = CreateINotifySocket();
+ // watch for files created and deleted in "/dev"
+ inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
+
+ // initialize uevent watcher
+ uevent_fd = CreateUEventSocket();
+ if (uevent_fd < 0)
+ {
+ LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
+ return NULL;
+ }
+
+ while (1)
+ {
+ struct pollfd fds[2];
+ int timeout, result;
+
+#define INOTIFY_IDX 0
+#define UEVENT_IDX 1
+
+ fds[INOTIFY_IDX].fd = inotify_fd;
+ fds[INOTIFY_IDX].events = POLLIN;
+ fds[INOTIFY_IDX].revents = 0;
+ fds[UEVENT_IDX].fd = uevent_fd;
+ fds[UEVENT_IDX].events = POLLIN;
+ fds[UEVENT_IDX].revents = 0;
+
+ // wait for an event or a timeout to occur.
+ // poll() can also return in response to a SIGUSR1 signal
+ timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
+ result = poll(fds, 2, timeout);
+
+ // lock the mutex while we are handling events
+ pthread_mutex_lock(&sMutex);
+
+ // handle inotify notifications for block device creation and deletion
+ if (fds[INOTIFY_IDX].revents == POLLIN)
+ {
+ struct inotify_event event;
+ char buffer[512];
+ int length = read(inotify_fd, buffer, sizeof(buffer));
+ int offset = 0;
+
+ while (length >= (int)sizeof(struct inotify_event))
+ {
+ struct inotify_event* event = (struct inotify_event *)&buffer[offset];
+
+ if (event->mask == IN_CREATE)
+ {
+ LOG_MOUNT("/dev/block/%s created\n", event->name);
+ HandleMediaInserted(event->name);
+ }
+ else if (event->mask == IN_DELETE)
+ {
+ LOG_MOUNT("/dev/block/%s deleted\n", event->name);
+ HandleMediaRemoved(event->name);
+ }
+
+ int size = sizeof(struct inotify_event) + event->len;
+ length -= size;
+ offset += size;
+ }
+ }
+
+ // handle uevent notifications for USB state changes
+ if (fds[UEVENT_IDX].revents == POLLIN)
+ {
+ char buffer[64*1024];
+ int count;
+
+ count = recv(uevent_fd, buffer, sizeof(buffer), 0);
+ if (count > 0) {
+ char* s = buffer;
+ char* end = s + count;
+ char* type = NULL;
+ char* online = NULL;
+ char* switchName = NULL;
+ char* switchState = NULL;
+
+ while (s < end) {
+ if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
+ type = s + strlen("POWER_SUPPLY_TYPE=");
+ else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
+ online = s + strlen("POWER_SUPPLY_ONLINE=");
+ else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
+ switchName = s + strlen("SWITCH_NAME=");
+ else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
+ switchState = s + strlen("SWITCH_STATE=");
+ s += (strlen(s) + 1);
+ }
+
+ // we use the usb_mass_storage switch state to tell us when USB is online
+ if (switchName && switchState &&
+ !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
+ {
+ LOG_MOUNT("USB online\n");
+ HandleMassStorageOnline(true);
+ }
+
+ // and we use the power supply state to tell us when USB is offline
+ // we can't rely on the switch for offline detection because we get false positives
+ // when USB is reenumerated by the host.
+ if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
+ {
+ LOG_MOUNT("USB offline\n");
+ HandleMassStorageOnline(false);
+ }
+ }
+ }
+
+ // handle retries
+ if (sRetriesPending)
+ HandleRetries();
+
+ // done handling events, so unlock the mutex
+ pthread_mutex_unlock(&sMutex);
+ }
+
+ inotify_rm_watch(inotify_fd, id);
+ close(inotify_fd);
+ close(uevent_fd);
+
+ return NULL;
+}
+
+/*****************************************************
+ *
+ * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
+ *
+ *****************************************************/
+
+// Called to enable or disable USB mass storage support
+void EnableMassStorage(boolean enable)
+{
+ pthread_mutex_lock(&sMutex);
+
+ LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
+ gMassStorageEnabled = enable;
+ MassStorageStateChanged();
+ pthread_mutex_unlock(&sMutex);
+ }
+
+// Called to request that the specified mount point be mounted
+void MountMedia(const char* mountPoint)
+{
+ MountPoint* mp = sMountPointList;
+
+ LOG_MOUNT("MountMedia(%s)\n", mountPoint);
+
+ pthread_mutex_lock(&sMutex);
+ while (mp)
+ {
+ if (strcmp(mp->mountPoint, mountPoint) == 0)
+ {
+ if (mp->state == kUnmountingForEject)
+ {
+ // handle the case where we try to remount before we actually unmounted
+ ClearRetries(mp, kMounted);
+ }
+
+ // don't attempt to mount if mass storage is active
+ if (!MassStorageEnabledForMountPoint(mp))
+ RequestMount(mp);
+ }
+
+ mp = mp->next;
+ }
+ pthread_mutex_unlock(&sMutex);
+ }
+
+// Called to request that the specified mount point be unmounted
+void UnmountMedia(const char* mountPoint)
+{
+ MountPoint* mp = sMountPointList;
+
+ pthread_mutex_lock(&sMutex);
+ while (mp)
+ {
+ if (strcmp(mp->mountPoint, mountPoint) == 0)
+ RequestUnmount(mp, kUnmountingForEject);
+
+ mp = mp->next;
+ }
+ pthread_mutex_unlock(&sMutex);
+}
+
+boolean IsMassStorageEnabled()
+{
+ return gMassStorageEnabled;
+}
+
+boolean IsMassStorageConnected()
+{
+ return gMassStorageConnected;
+}
+
+/***********************************************
+ *
+ * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
+ *
+ ***********************************************/
+
+void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms)
+{
+ MountPoint* newMountPoint;
+
+ 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;
+ 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;
+ while (mp)
+ {
+ RequestMount(mp);
+ mp = mp->next;
+ }
+}
+
+void StartAutoMounter()
+{
+ gExcludedPids[0] = getpid();
+
+ gMassStorageConnected = ReadMassStorageState();
+ LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
+
+ MountDevices();
+ pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
+}
diff --git a/mountd/MODULE_LICENSE_APACHE2 b/mountd/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mountd/MODULE_LICENSE_APACHE2
diff --git a/mountd/NOTICE b/mountd/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/mountd/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/mountd/ProcessKiller.c b/mountd/ProcessKiller.c
new file mode 100644
index 0000000..e377774
--- /dev/null
+++ b/mountd/ProcessKiller.c
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+/*
+** mountd process killer
+*/
+
+#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 <sys/stat.h>
+
+
+static boolean ReadSymLink(const char* path, char* link)
+{
+ struct stat s;
+ int length;
+
+ if (lstat(path, &s) < 0)
+ return false;
+ if ((s.st_mode & S_IFMT) != S_IFLNK)
+ return false;
+
+ // we have a symlink
+ length = readlink(path, link, PATH_MAX - 1);
+ if (length <= 0)
+ return false;
+ link[length] = 0;
+ return true;
+}
+
+static boolean PathMatchesMountPoint(const char* path, const char* mountPoint)
+{
+ int length = strlen(mountPoint);
+ if (length > 1 && strncmp(path, mountPoint, length) == 0)
+ {
+ // we need to do extra checking if mountPoint does not end in a '/'
+ if (mountPoint[length - 1] == '/')
+ return true;
+ // if mountPoint does not have a trailing slash, we need to make sure
+ // there is one in the path to avoid partial matches.
+ return (path[length] == 0 || path[length] == '/');
+ }
+
+ return false;
+}
+
+static void GetProcessName(int pid, char buffer[PATH_MAX])
+{
+ int fd;
+ sprintf(buffer, "/proc/%d/cmdline", pid);
+ fd = open(buffer, O_RDONLY);
+ if (fd < 0) {
+ strcpy(buffer, "???");
+ } else {
+ int length = read(fd, buffer, PATH_MAX - 1);
+ buffer[length] = 0;
+ close(fd);
+ }
+}
+
+static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
+{
+ DIR* dir;
+ struct dirent* de;
+ boolean fileOpen = false;
+ char path[PATH_MAX];
+ char link[PATH_MAX];
+ int parent_length;
+
+ // compute path to process's directory of open files
+ sprintf(path, "/proc/%d/fd", pid);
+ dir = opendir(path);
+ if (!dir)
+ return false;
+
+ // remember length of the path
+ parent_length = strlen(path);
+ // append a trailing '/'
+ path[parent_length++] = '/';
+
+ while ((de = readdir(dir)) != 0 && !fileOpen) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ // append the file name, after truncating to parent directory
+ path[parent_length] = 0;
+ strcat(path, de->d_name);
+
+ if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
+ {
+ char name[PATH_MAX];
+ GetProcessName(pid, name);
+ LOG_ERROR("process %s (%d) has open file %s\n", name, pid, link);
+ fileOpen = true;
+ }
+ }
+
+ closedir(dir);
+ return fileOpen;
+}
+
+static boolean CheckFileMaps(int pid, const char* mountPoint)
+{
+ FILE* file;
+ char buffer[PATH_MAX + 100];
+ boolean mapOpen = false;
+
+ sprintf(buffer, "/proc/%d/maps", pid);
+ file = fopen(buffer, "r");
+ if (!file)
+ return false;
+
+ while (!mapOpen && fgets(buffer, sizeof(buffer), file))
+ {
+ // skip to the path
+ const char* path = strchr(buffer, '/');
+ if (path && PathMatchesMountPoint(path, mountPoint))
+ {
+ char name[PATH_MAX];
+ GetProcessName(pid, name);
+ LOG_ERROR("process %s (%d) has open file map for %s\n", name, pid, path);
+ mapOpen = true;
+ }
+ }
+
+ fclose(file);
+ return mapOpen;
+}
+
+static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
+{
+ char path[PATH_MAX];
+ char link[PATH_MAX];
+
+ sprintf(path, "/proc/%d/%s", pid, name);
+ if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
+ {
+ char name[PATH_MAX];
+ GetProcessName(pid, name);
+ LOG_ERROR("process %s (%d) has %s in %s\n", name, pid, message, mountPoint);
+ return true;
+ }
+ else
+ return false;
+}
+
+static int get_pid(const char* s)
+{
+ int result = 0;
+ while (*s) {
+ if (!isdigit(*s)) return -1;
+ result = 10 * result + (*s++ - '0');
+ }
+ return result;
+}
+
+// hunt down and kill processes that have files open on the given mount point
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded)
+{
+ DIR* dir;
+ struct dirent* de;
+
+ LOG_ERROR("KillProcessesWithOpenFiles %s\n", mountPoint);
+ dir = opendir("/proc");
+ if (!dir) return;
+
+ while ((de = readdir(dir)) != 0)
+ {
+ boolean killed = false;
+ // does the name look like a process ID?
+ int pid = get_pid(de->d_name);
+ if (pid == -1) continue;
+
+ if (CheckFileDescriptorSymLinks(pid, mountPoint) // check for open files
+ || CheckFileMaps(pid, mountPoint) // check for mmap()
+ || CheckSymLink(pid, mountPoint, "cwd", "working directory") // check working directory
+ || CheckSymLink(pid, mountPoint, "root", "chroot") // check for chroot()
+ || CheckSymLink(pid, mountPoint, "exe", "executable path") // check executable path
+ )
+ {
+ 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));
+ }
+ }
+ }
+
+ closedir(dir);
+}
diff --git a/mountd/Server.c b/mountd/Server.c
new file mode 100644
index 0000000..64459bd
--- /dev/null
+++ b/mountd/Server.c
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+/*
+** mountd server support
+*/
+
+#include "mountd.h"
+#include "ASEC.h"
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include <pthread.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <private/android_filesystem_config.h>
+
+
+// current client file descriptor
+static int sFD = -1;
+
+// to synchronize writing to client
+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;
+
+ pthread_mutex_lock(&sWriteMutex);
+
+ LOG_SERVER("Write: %s\n", message);
+ if (sFD >= 0)
+ result = write(sFD, message, strlen(message) + 1);
+
+ pthread_mutex_unlock(&sWriteMutex);
+
+ return result;
+}
+
+static int Write2(const char* message, const char* data)
+{
+ int result = -1;
+
+ char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
+ if (!buffer)
+ {
+ LOG_ERROR("alloca failed in Write2\n");
+ return -1;
+ }
+
+ strcpy(buffer, message);
+ strcat(buffer, data);
+ return Write(buffer);
+}
+
+static void SendStatus()
+{
+ Write(IsMassStorageConnected() ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
+ Write(IsMassStorageEnabled() ? MOUNTD_UMS_ENABLED : MOUNTD_UMS_DISABLED);
+}
+
+static void DoCommand(const char* command)
+{
+ LOG_SERVER("DoCommand %s\n", command);
+
+ if (strcmp(command, MOUNTD_ENABLE_UMS) == 0)
+ {
+ EnableMassStorage(true);
+ Write(MOUNTD_UMS_ENABLED);
+ }
+ else if (strcmp(command, MOUNTD_DISABLE_UMS) == 0)
+ {
+ EnableMassStorage(false);
+ Write(MOUNTD_UMS_DISABLED);
+ }
+ else if (strcmp(command, MOUNTD_SEND_STATUS) == 0)
+ {
+ SendStatus();
+ }
+ else if (strncmp(command, MOUNTD_MOUNT_MEDIA, strlen(MOUNTD_MOUNT_MEDIA)) == 0)
+ {
+ const char* path = command + strlen(MOUNTD_MOUNT_MEDIA);
+ MountMedia(path);
+ }
+ else if (strncmp(command, MOUNTD_EJECT_MEDIA, strlen(MOUNTD_EJECT_MEDIA)) == 0)
+ {
+ 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);
+}
+
+int RunServer()
+{
+ int socket = android_get_control_socket(MOUNTD_SOCKET);
+ if (socket < 0) {
+ LOGE("Obtaining file descriptor for socket '%s' failed: %s",
+ MOUNTD_SOCKET, strerror(errno));
+ return -1;
+ }
+
+ if (listen(socket, 4) < 0) {
+ LOGE("Unable to listen on file descriptor '%d' for socket '%s': %s",
+ socket, MOUNTD_SOCKET, strerror(errno));
+ return -1;
+ }
+
+ while (1)
+ {
+ struct sockaddr addr;
+ socklen_t alen;
+ struct ucred cred;
+ socklen_t size;
+
+ alen = sizeof(addr);
+ sFD = accept(socket, &addr, &alen);
+ if (sFD < 0)
+ continue;
+
+ if (sDeferredUnmountableMediaPath) {
+ NotifyMediaState(sDeferredUnmountableMediaPath, MEDIA_UNMOUNTABLE, false);
+ free(sDeferredUnmountableMediaPath);
+ 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];
+ int result = read(sFD, buffer, sizeof(buffer) - 1);
+ if (result > 0)
+ {
+ int start = 0;
+ int i;
+ // command should be zero terminated, but just in case
+ buffer[result] = 0;
+ for (i = 0; i < result; i++)
+ {
+ if (buffer[i] == 0)
+ {
+ DoCommand(buffer + start);
+ start = i + 1;
+ }
+ }
+ }
+ else
+ {
+ close(sFD);
+ sFD = -1;
+ break;
+ }
+ }
+ }
+
+ // should never get here
+ return 0;
+}
+
+void SendMassStorageConnected(boolean connected)
+{
+ Write(connected ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
+}
+
+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;
+ const char* propertyValue = NULL;
+
+ switch (state) {
+ case MEDIA_REMOVED:
+ event = MOUNTD_MEDIA_REMOVED;
+ propertyValue = EXTERNAL_STORAGE_REMOVED;
+ break;
+ case MEDIA_UNMOUNTED:
+ event = MOUNTD_MEDIA_UNMOUNTED;
+ propertyValue = EXTERNAL_STORAGE_UNMOUNTED;
+ break;
+ case MEDIA_MOUNTED:
+ event = (readOnly ? MOUNTD_MEDIA_MOUNTED_READ_ONLY : MOUNTD_MEDIA_MOUNTED);
+ propertyValue = (readOnly ? EXTERNAL_STORAGE_MOUNTED_READ_ONLY : EXTERNAL_STORAGE_MOUNTED);
+ break;
+ case MEDIA_SHARED:
+ event = MOUNTD_MEDIA_SHARED;
+ propertyValue = EXTERNAL_STORAGE_SHARED;
+ break;
+ case MEDIA_BAD_REMOVAL:
+ event = MOUNTD_MEDIA_BAD_REMOVAL;
+ propertyValue = EXTERNAL_STORAGE_BAD_REMOVAL;
+ break;
+ case MEDIA_UNMOUNTABLE:
+ event = MOUNTD_MEDIA_UNMOUNTABLE;
+ propertyValue = EXTERNAL_STORAGE_UNMOUNTABLE;
+ break;
+ default:
+ LOG_ERROR("unknown MediaState %d in NotifyMediaState\n", state);
+ return;
+ }
+
+ property_set(EXTERNAL_STORAGE_STATE, propertyValue);
+ int result = Write2(event, path);
+ if (result < 0 && state == MEDIA_UNMOUNTABLE) {
+
+ // if we cannot communicate with the runtime, defer this message until the runtime is available
+ sDeferredUnmountableMediaPath = strdup(path);
+ }
+}
diff --git a/mountd/logwrapper.c b/mountd/logwrapper.c
new file mode 100644
index 0000000..69606ab
--- /dev/null
+++ b/mountd/logwrapper.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+
+int parent(const char *tag, int parent_read) {
+ int status;
+ char buffer[4096];
+
+ int a = 0; // start index of unprocessed data
+ int b = 0; // end index of unprocessed data
+ int sz;
+ while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
+
+ sz += b;
+ // Log one line at a time
+ for (b = 0; b < sz; b++) {
+ if (buffer[b] == '\r') {
+ buffer[b] = '\0';
+ } else if (buffer[b] == '\n') {
+ buffer[b] = '\0';
+ LOG(LOG_INFO, tag, &buffer[a]);
+ a = b + 1;
+ }
+ }
+
+ if (a == 0 && b == sizeof(buffer) - 1) {
+ // buffer is full, flush
+ buffer[b] = '\0';
+ LOG(LOG_INFO, tag, &buffer[a]);
+ b = 0;
+ } else if (a != b) {
+ // Keep left-overs
+ b -= a;
+ memmove(buffer, &buffer[a], b);
+ a = 0;
+ } else {
+ a = 0;
+ b = 0;
+ }
+
+ }
+ // Flush remaining data
+ if (a != b) {
+ buffer[b] = '\0';
+ LOG(LOG_INFO, tag, &buffer[a]);
+ }
+ status = 0xAAAA;
+ if (wait(&status) != -1) { // Wait for child
+ if (WIFEXITED(status)) {
+ LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
+ WEXITSTATUS(status));
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status))
+ LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
+ WTERMSIG(status));
+ else if (WIFSTOPPED(status))
+ LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
+ WSTOPSIG(status));
+ } else
+ LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
+ strerror(errno), errno);
+ return -EAGAIN;
+}
+
+void child(int argc, char* argv[]) {
+ // create null terminated argv_child array
+ char* argv_child[argc + 1];
+ memcpy(argv_child, argv, argc * sizeof(char *));
+ argv_child[argc] = NULL;
+
+ // XXX: PROTECT FROM VIKING KILLER
+ if (execvp(argv_child[0], argv_child)) {
+ LOG(LOG_ERROR, "logwrapper",
+ "executing %s failed: %s\n", argv_child[0], strerror(errno));
+ exit(-1);
+ }
+}
+
+int logwrap(int argc, char* argv[])
+{
+ pid_t pid;
+
+ int parent_ptty;
+ int child_ptty;
+ char *child_devname = NULL;
+
+ /* Use ptty instead of socketpair so that STDOUT is not buffered */
+ parent_ptty = open("/dev/ptmx", O_RDWR);
+ if (parent_ptty < 0) {
+ LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty\n");
+ return -errno;
+ }
+
+ if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+ ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+ LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx\n");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ LOG(LOG_ERROR, "logwrapper", "Failed to fork\n");
+ return -errno;
+ } else if (pid == 0) {
+ child_ptty = open(child_devname, O_RDWR);
+ if (child_ptty < 0) {
+ LOG(LOG_ERROR, "logwrapper", "Problem with child ptty\n");
+ return -errno;
+ }
+
+ // redirect stdout and stderr
+ close(parent_ptty);
+ dup2(child_ptty, 1);
+ dup2(child_ptty, 2);
+ close(child_ptty);
+
+ child(argc, argv);
+ } else {
+ // switch user and group to "log"
+ // this may fail if we are not root,
+ // but in that case switching user/group is unnecessary
+
+ // setgid(AID_LOG);
+ // setuid(AID_LOG);
+
+ return parent(argv[0], parent_ptty);
+ }
+
+ return 0;
+}
diff --git a/mountd/mountd.c b/mountd/mountd.c
new file mode 100644
index 0000000..27ec8de
--- /dev/null
+++ b/mountd/mountd.c
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+/*
+** mountd main program
+*/
+
+#include "mountd.h"
+
+#include <cutils/config_utils.h>
+#include <cutils/cpu_info.h>
+#include <cutils/properties.h>
+
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
+#include <private/android_filesystem_config.h>
+
+#ifdef MOUNTD_LOG
+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)
+{
+ cnode* root = config_node("", "");
+ cnode* node;
+
+ config_load_file(root, path);
+ node = root->first_child;
+
+ while (node)
+ {
+ if (strcmp(node->name, "mount") == 0)
+ {
+ 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 (!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;
+
+ child = child->next;
+ }
+
+ // mount point and removable fields are optional
+ if (block_device && mount_point)
+ {
+ 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);
+ }
+ }
+ }
+
+ node = node->next;
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ const char* configPath = "/system/etc/mountd.conf";
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ const char* arg = argv[i];
+
+ if (strcmp(arg, "-f") == 0)
+ {
+ if (i < argc - 1)
+ configPath = argv[++i];
+ }
+ }
+
+ ReadConfigFile(configPath);
+ StartAutoMounter();
+ return RunServer();
+}
diff --git a/mountd/mountd.h b/mountd/mountd.h
new file mode 100644
index 0000000..c4bc91d
--- /dev/null
+++ b/mountd/mountd.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOUNTD_H__
+#define MOUNTD_H__
+
+#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
+
+// 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); }
+#else
+#define LOG_ERROR(fmt, args...) \
+ do { } while (0)
+#endif /* ENABLE_LOG_ERROR */
+
+#ifdef ENABLE_LOG_MOUNT
+#define LOG_MOUNT(fmt, args...) \
+ { LOGD(fmt , ## args); }
+#else
+#define LOG_MOUNT(fmt, args...) \
+ do { } while (0)
+#endif /* ENABLE_LOG_MOUNT */
+
+#ifdef ENABLE_LOG_SERVER
+#define LOG_SERVER(fmt, args...) \
+ { LOGD(fmt , ## args); }
+#else
+#define LOG_SERVER(fmt, args...) \
+ 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
+ MEDIA_REMOVED,
+
+ // media in SD card slot, but not mounted
+ MEDIA_UNMOUNTED,
+
+ // media in SD card slot and mounted at its mount point
+ MEDIA_MOUNTED,
+
+ // media in SD card slot, unmounted, and shared as a mass storage device
+ MEDIA_SHARED,
+
+ // media was removed from SD card slot, but mount point was not unmounted
+ // this state is cleared after the mount point is unmounted
+ MEDIA_BAD_REMOVAL,
+
+ // media in SD card slot could not be mounted (corrupt file system?)
+ MEDIA_UNMOUNTABLE,
+} MediaState;
+
+// socket name for connecting to mountd
+#define MOUNTD_SOCKET "mountd"
+
+// mountd commands
+// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
+#define MOUNTD_ENABLE_UMS "enable_ums"
+#define MOUNTD_DISABLE_UMS "disable_ums"
+#define MOUNTD_SEND_STATUS "send_status"
+
+// these commands should contain a mount point following the colon
+#define MOUNTD_MOUNT_MEDIA "mount_media:"
+#define MOUNTD_EJECT_MEDIA "eject_media:"
+
+// mountd events
+// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
+#define MOUNTD_UMS_ENABLED "ums_enabled"
+#define MOUNTD_UMS_DISABLED "ums_disabled"
+#define MOUNTD_UMS_CONNECTED "ums_connected"
+#define MOUNTD_UMS_DISCONNECTED "ums_disconnected"
+
+// these events correspond to the states in the MediaState enum.
+// a path to the mount point follows the colon.
+#define MOUNTD_MEDIA_REMOVED "media_removed:"
+#define MOUNTD_MEDIA_UNMOUNTED "media_unmounted:"
+#define MOUNTD_MEDIA_MOUNTED "media_mounted:"
+#define MOUNTD_MEDIA_MOUNTED_READ_ONLY "media_mounted_ro:"
+#define MOUNTD_MEDIA_SHARED "media_shared:"
+#define MOUNTD_MEDIA_BAD_REMOVAL "media_bad_removal:"
+#define MOUNTD_MEDIA_UNMOUNTABLE "media_unmountable:"
+
+// this event sent to request unmount for media mount point
+#define MOUNTD_REQUEST_EJECT "request_eject:"
+
+// system properties
+// these must match the corresponding strings in //device/java/android/android/os/Environment.java
+#define EXTERNAL_STORAGE_STATE "EXTERNAL_STORAGE_STATE"
+#define EXTERNAL_STORAGE_REMOVED "removed"
+#define EXTERNAL_STORAGE_UNMOUNTED "unmounted"
+#define EXTERNAL_STORAGE_MOUNTED "mounted"
+#define EXTERNAL_STORAGE_MOUNTED_READ_ONLY "mounted_ro"
+#define EXTERNAL_STORAGE_SHARED "shared"
+#define EXTERNAL_STORAGE_BAD_REMOVAL "bad_removal"
+#define EXTERNAL_STORAGE_UNMOUNTABLE "unmountable"
+
+// AutoMount.c
+
+boolean IsMassStorageEnabled();
+boolean IsMassStorageConnected();
+
+void MountMedia(const char* mountPoint);
+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, 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();
+
+// check /proc/mounts for mounted file systems, and notify mount or unmount for any that are in our automount list
+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, pid_t *excluded, int num_excluded);
+
+// logwrapper.c
+int logwrap(int argc, char* argv[]);
+
+// Server.c
+
+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__