diff options
Diffstat (limited to 'mountd/AutoMount.c')
-rw-r--r-- | mountd/AutoMount.c | 1062 |
1 files changed, 1062 insertions, 0 deletions
diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c new file mode 100644 index 000000000..12ad572e9 --- /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); +} |