blob: 0aac871d5f57bbb49bef386725c6dbed46f82e4a [file] [log] [blame]
/*
* 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/fsck_msdos";
// 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): fsck_msdos not found (skipping checks)\n", device);
return 0;
}
sprintf(cmdline, "%s -p %s", FSCK_MSDOS_PATH, device);
LOG_MOUNT("Checking filesystem (%s)\n", cmdline);
// XXX: Notify framework we're disk checking
// XXX: PROTECT FROM VIKING KILLER
if ((rc = system(cmdline)) < 0) {
LOG_ERROR("Error executing disk check command (%d)\n", errno);
return -errno;
}
rc = WEXITSTATUS(rc);
if (rc == 0) {
LOG_MOUNT("Filesystem check completed OK\n");
return 0;
} else if (rc == 1) {
LOG_MOUNT("Filesystem check failed (invalid usage)\n");
return -EINVAL;
} else if (rc == 2) {
LOG_MOUNT("Filesystem check failed (unresolved issues)\n");
return -EIO;
} else if (rc == 4) {
LOG_MOUNT("Filesystem check failed (root changed)\n");
return -EIO;
} else if (rc == 8) {
LOG_MOUNT("Filesystem check failed (general failure)\n");
return -EIO;
} else if (rc == 12) {
LOG_MOUNT("Filesystem check failed (exit signaled)\n");
return -EIO;
} else {
LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc);
return -EIO;
}
}
static int DoMountDevice(const char* device, const char* mountPoint)
{
LOG_MOUNT("mounting %s at %s\n", device, mountPoint);
#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 != 0)
return result;
if ((result = CheckFilesystem(device))) {
LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result);
// XXX: Notify framework - need a new SDCARD state for the following:
// - SD cards which are not present
// - SD cards with no partition table
// - SD cards with no filesystem
// - SD cards with bad filesystem
return result;
}
// Extra safety measures:
flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
// Also, set fmask = 711 so that files cannot be marked executable,
// 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");
}
LOG_MOUNT("mount returned %d errno: %d\n", result, errno);
if (result == 0) {
NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
MountPoint* mp = sMountPointList;
while (mp) {
if (!strcmp(mountPoint, mp->mountPoint)) {
int i;
for (i = 0; i < ASEC_STORES_MAX; i++) {
if (mp->asecHandles[i] != NULL) {
int a_result;
if ((a_result = AsecStart(mp->asecHandles[i])) < 0) {
LOG_ERROR("ASEC start failure (%d)\n", a_result);
}
}
}
break;
}
mp = mp -> next;
}
} else if (errno == EBUSY) {
// ignore EBUSY, since it usually means the device is already mounted
result = 0;
} else {
#if CREATE_MOUNT_POINTS
rmdir(mountPoint);
#endif
LOG_MOUNT("mount failed, errno: %d\n", errno);
}
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++)
{
snprintf(buf, sizeof(buf), "%sp%d", device, i);
if (DoMountDevice(buf, mountPoint) == 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);
}