/* * 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 #include #include #include #include #include #include #include #include #include // 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); } }