From 726a0206326ca4cf22aa7c59e174a83a0da9727f Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Tue, 16 Dec 2014 20:01:38 -0600 Subject: MTP add/remove storage instead of disabling MTP Implement a pipe between TWRP and MTP to allow TWRP to tell MTP to remove storage partitions as they become unavailable (e.g. during a wipe, unmount, etc) instead of disabling MTP completely. This includes some fixes and improvements in destructors to properly remove / delete various items. This also means that we will not be toggling adb off and on quite as often. I do not like that we had to add another thread, but we were unable to use select() on the mtp_usb character device because this device does not support polling. Select always returned indicating that the mtp file descriptor was ready to be read and the resulting read would block. The read block prevented us from being able to include reading of the pipe between TWRP and MTP in the main MTP thread. We might want to add a return pipe letting TWRP know if the removal of the storage device was successful, but I am not sure how we want to implement this. It would invovle timeouts in both TWRP and MTP to ensure that we returned a failure indicator in a timely manner to TWRP and prevent deleting the storage device in the case of a failure. Right now we make no attempt to ensure that an MTP operation is underway like a large file transfer, but we were not doing anything like this in the past. In some respects we have limited control over what happens. If the user installs a zip that unmounts a storage partition, we will not know about the change in storage status anyway. Regular Android does not have these troubles because partitions rarely get unmounted like in recovery. At some point, we have to hold the user accountable for performing actions that may remove a storage partition while they are using MTP anyway. Ideally we do not want to toggle the USB IDs and thus toggle adb off and on during early boot, but I am not sure what the best way to handle that at this time. Change-Id: I9343e5396bf6023d3b994de1bf01ed91d129bc14 --- gui/action.cpp | 1 + gui/partitionlist.cpp | 1 + mtp/MtpDatabase.h | 1 + mtp/MtpMessage.hpp | 33 ++++++++++ mtp/MtpServer.cpp | 29 ++++++++- mtp/MtpStorage.cpp | 58 +++++++++++++---- mtp/MtpStorage.h | 1 + mtp/btree.cpp | 1 + mtp/mtp_MtpDatabase.cpp | 7 +++ mtp/mtp_MtpDatabase.hpp | 1 + mtp/mtp_MtpServer.cpp | 52 ++++++++++++++- mtp/mtp_MtpServer.hpp | 5 ++ mtp/tw_sys_atomics.h | 67 ++++++++++++++++++++ mtp/twrpMtp.cpp | 7 ++- mtp/twrpMtp.hpp | 3 +- partition.cpp | 26 ++++---- partitionmanager.cpp | 163 +++++++++++++++++++++++++++++++++++++++++++----- partitions.hpp | 8 +++ 18 files changed, 420 insertions(+), 44 deletions(-) create mode 100644 mtp/MtpMessage.hpp create mode 100644 mtp/tw_sys_atomics.h diff --git a/gui/action.cpp b/gui/action.cpp index 27fd7a5d7..ff2c2e00f 100644 --- a/gui/action.cpp +++ b/gui/action.cpp @@ -462,6 +462,7 @@ int GUIAction::doAction(Action action, int isThreaded /* = 0 */) gui_print("Simulating actions...\n"); } else if (!simulate) { PartitionManager.Mount_By_Path(arg, true); + PartitionManager.Add_MTP_Storage(arg); } else gui_print("Simulating actions...\n"); return 0; diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp index 2d464e1b6..9cc6a777b 100644 --- a/gui/partitionlist.cpp +++ b/gui/partitionlist.cpp @@ -749,6 +749,7 @@ int GUIPartitionList::NotifyTouch(TOUCH_STATE state, int x, int y) if (!mList.at(actualSelection).selected) { if (PartitionManager.Mount_By_Path(mList.at(actualSelection).Mount_Point, true)) { mList.at(actualSelection).selected = 1; + PartitionManager.Add_MTP_Storage(mList.at(actualSelection).Mount_Point); mUpdate = 1; } } else { diff --git a/mtp/MtpDatabase.h b/mtp/MtpDatabase.h index c25e9b24c..a0ff8dace 100755 --- a/mtp/MtpDatabase.h +++ b/mtp/MtpDatabase.h @@ -61,6 +61,7 @@ public: virtual MtpDevicePropertyList* getSupportedDeviceProperties() = 0; virtual void createDB(MtpStorage* storage, MtpStorageID storageID) = 0; + virtual void destroyDB(MtpStorageID storageID) = 0; virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, diff --git a/mtp/MtpMessage.hpp b/mtp/MtpMessage.hpp new file mode 100644 index 000000000..272da1743 --- /dev/null +++ b/mtp/MtpMessage.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 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. + * + * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++ + */ + +#ifndef _MTPMESSAGE_HPP +#define _MTPMESSAGE_HPP + +#define MTP_MESSAGE_ADD_STORAGE 1 +#define MTP_MESSAGE_REMOVE_STORAGE 2 + +struct mtpmsg { + int message_type; // 1 is add, 2 is remove, see above + unsigned int storage_id; + const char* display; + const char* path; + uint64_t maxFileSize; +}; + +#endif //_MTPMESSAGE_HPP diff --git a/mtp/MtpServer.cpp b/mtp/MtpServer.cpp index f4af2b948..f99554b03 100755 --- a/mtp/MtpServer.cpp +++ b/mtp/MtpServer.cpp @@ -116,9 +116,13 @@ MtpServer::~MtpServer() { } void MtpServer::addStorage(MtpStorage* storage) { + android::Mutex::Autolock autoLock(mMutex); MTPD("addStorage(): storage: %x\n", storage); + if (getStorage(storage->getStorageID()) != NULL) { + MTPE("MtpServer::addStorage Storage for storage ID %i already exists.\n", storage->getStorageID()); + return; + } mDatabase->createDB(storage, storage->getStorageID()); - android::Mutex::Autolock autoLock(mMutex); mStorages.push(storage); sendStoreAdded(storage->getStorageID()); } @@ -128,11 +132,31 @@ void MtpServer::removeStorage(MtpStorage* storage) { for (size_t i = 0; i < mStorages.size(); i++) { if (mStorages[i] == storage) { + MTPD("MtpServer::removeStorage calling sendStoreRemoved\n"); + // First lock the mutex so that the inotify thread and main + // thread do not do anything while we remove the storage + // item, and to make sure we don't remove the item while an + // operation is in progress + mDatabase->lockMutex(); + // Grab the storage ID before we delete the item from the + // database + MtpStorageID storageID = storage->getStorageID(); + // Remove the item from the mStorages from the vector. At + // this point the main thread will no longer be able to find + // this storage item anymore. mStorages.removeAt(i); - sendStoreRemoved(storage->getStorageID()); + // Destroy the storage item, free up all the memory, kill + // the inotify thread. + mDatabase->destroyDB(storageID); + // Tell the host OS that the storage item is gone. + sendStoreRemoved(storageID); + // Unlock any remaining mutexes on other storage devices. + // If no storage devices exist anymore this will do nothing. + mDatabase->unlockMutex(); break; } } + MTPD("MtpServer::removeStorage DONE\n"); } MtpStorage* MtpServer::getStorage(MtpStorageID id) { @@ -275,6 +299,7 @@ void MtpServer::sendStoreAdded(MtpStorageID id) { void MtpServer::sendStoreRemoved(MtpStorageID id) { MTPD("sendStoreRemoved %08X\n", id); sendEvent(MTP_EVENT_STORE_REMOVED, id); + MTPD("MtpServer::sendStoreRemoved done\n"); } void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) { diff --git a/mtp/MtpStorage.cpp b/mtp/MtpStorage.cpp index 319be094b..ab4f8e044 100755 --- a/mtp/MtpStorage.cpp +++ b/mtp/MtpStorage.cpp @@ -36,6 +36,7 @@ #include #include #include +#include "tw_sys_atomics.h" #define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY ) @@ -54,6 +55,8 @@ MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, MTPI("MtpStorage id: %d path: %s\n", id, filePath); inotify_thread = 0; inotify_fd = -1; + // Threading has not started yet so we should be safe to set these directly instead of using atomics + inotify_thread_kill = 0; sendEvents = false; handleCurrentlySending = 0; use_mutex = true; @@ -63,25 +66,32 @@ MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, } if (pthread_mutex_init(&inMutex, NULL) != 0) { MTPE("Failed to init inMutex\n"); + pthread_mutex_destroy(&mtpMutex); use_mutex = false; } - } MtpStorage::~MtpStorage() { if (inotify_thread) { - // TODO: what does this do? manpage says it does not kill the thread - pthread_kill(inotify_thread, 0); + __tw_atomic_cmpxchg(0, 1, &inotify_thread_kill); + //inotify_thread_kill = 1; + MTPD("joining inotify_thread after sending the kill notification.\n"); + pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here + inotify_thread = 0; + MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n"); for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { inotify_rm_watch(inotify_fd, i->first); } close(inotify_fd); + inotifymap.clear(); } - for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { - delete i->second; - } + // Deleting the root tree causes a cascade in btree.cpp that ends up + // deleting all of the trees and nodes. + delete mtpmap[0]; + mtpmap.clear(); if (use_mutex) { use_mutex = false; + MTPD("~MtpStorage destroying mutexes\n"); pthread_mutex_destroy(&mtpMutex); pthread_mutex_destroy(&inMutex); } @@ -566,9 +576,22 @@ int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty pthread_t MtpStorage::inotify(void) { pthread_t thread; + pthread_attr_t tattr; + + if (pthread_attr_init(&tattr)) { + MTPE("Unable to pthread_attr_init\n"); + return 0; + } + if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) { + MTPE("Error setting pthread_attr_setdetachstate\n"); + return 0; + } ThreadPtr inotifyptr = &MtpStorage::inotify_t; PThreadPtr p = *(PThreadPtr*)&inotifyptr; - pthread_create(&thread, NULL, p, this); + pthread_create(&thread, &tattr, p, this); + if (pthread_attr_destroy(&tattr)) { + MTPE("Failed to pthread_attr_destroy\n"); + } return thread; } @@ -669,10 +692,20 @@ int MtpStorage::inotify_t(void) { #define EVENT_SIZE ( sizeof(struct inotify_event) ) #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) ) char buf[EVENT_BUF_LEN]; + fd_set fdset; + struct timeval seltmout; + int sel_ret; MTPD("inotify thread starting.\n"); - while (true) { + while (__tw_atomic_cmpxchg(0, inotify_thread_kill, &inotify_thread_kill) == 0) { + FD_ZERO(&fdset); + FD_SET(inotify_fd, &fdset); + seltmout.tv_sec = 0; + seltmout.tv_usec = 25000; + sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout); + if (sel_ret == 0) + continue; int i = 0; int len = read(inotify_fd, buf, EVENT_BUF_LEN); @@ -682,7 +715,7 @@ int MtpStorage::inotify_t(void) { MTPE("inotify_t Can't read inotify events\n"); } - while (i < len) { + while (i < len && __tw_atomic_cmpxchg(0, inotify_thread_kill, &inotify_thread_kill) == 0) { struct inotify_event *event = (struct inotify_event *) &buf[i]; if (event->len) { MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name); @@ -693,11 +726,12 @@ int MtpStorage::inotify_t(void) { i += EVENT_SIZE + event->len; } } - - for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { + MTPD("inotify_thread_kill received!\n"); + // This cleanup is handled in the destructor. + /*for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { inotify_rm_watch(inotify_fd, i->first); } - close(inotify_fd); + close(inotify_fd);*/ return 0; } diff --git a/mtp/MtpStorage.h b/mtp/MtpStorage.h index 418e3db8c..cdbb73b50 100755 --- a/mtp/MtpStorage.h +++ b/mtp/MtpStorage.h @@ -113,6 +113,7 @@ private: bool use_mutex; pthread_mutex_t inMutex; // inotify mutex pthread_mutex_t mtpMutex; // main mtp mutex + int inotify_thread_kill; }; #endif // _MTP_STORAGE_H diff --git a/mtp/btree.cpp b/mtp/btree.cpp index 3a5648db0..e53afab98 100755 --- a/mtp/btree.cpp +++ b/mtp/btree.cpp @@ -28,6 +28,7 @@ Tree::Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& na Tree::~Tree() { for (std::map::iterator it = entries.begin(); it != entries.end(); ++it) delete it->second; + entries.clear(); } int Tree::getCount(void) { diff --git a/mtp/mtp_MtpDatabase.cpp b/mtp/mtp_MtpDatabase.cpp index 05bb5d9fb..17053f1de 100755 --- a/mtp/mtp_MtpDatabase.cpp +++ b/mtp/mtp_MtpDatabase.cpp @@ -233,10 +233,17 @@ void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, } void MyMtpDatabase::createDB(MtpStorage* storage, MtpStorageID storageID) { + MTPD("MyMtpDatabase::createDB called\n"); storagemap[storageID] = storage; storage->createDB(); } +void MyMtpDatabase::destroyDB(MtpStorageID storageID) { + MtpStorage* storage = storagemap[storageID]; + storagemap.erase(storageID); + delete storage; +} + MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { diff --git a/mtp/mtp_MtpDatabase.hpp b/mtp/mtp_MtpDatabase.hpp index cc8097be2..49e59135a 100755 --- a/mtp/mtp_MtpDatabase.hpp +++ b/mtp/mtp_MtpDatabase.hpp @@ -64,6 +64,7 @@ public: virtual ~MyMtpDatabase(); void createDB(MtpStorage* storage, MtpStorageID storageID); + void destroyDB(MtpStorageID storageID); virtual MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, diff --git a/mtp/mtp_MtpServer.cpp b/mtp/mtp_MtpServer.cpp index 17facdd70..f49270fdf 100755 --- a/mtp/mtp_MtpServer.cpp +++ b/mtp/mtp_MtpServer.cpp @@ -25,11 +25,13 @@ #include #include #include +#include #include "mtp_MtpServer.hpp" #include "MtpServer.h" #include "MtpStorage.h" #include "MtpDebug.h" +#include "MtpMessage.hpp" #include @@ -37,6 +39,11 @@ void twmtp_MtpServer::start() { if (setup() == 0) { add_storage(); + MTPD("Starting add / remove mtppipe monitor thread\n"); + pthread_t thread; + ThreadPtr mtpptr = &twmtp_MtpServer::mtppipe_thread; + PThreadPtr p = *(PThreadPtr*)&mtpptr; + pthread_create(&thread, NULL, p, this); server->run(); } } @@ -140,9 +147,52 @@ void twmtp_MtpServer::remove_storage(int storageId) if (server) { MtpStorage* storage = server->getStorage(storageId); if (storage) { + MTPD("twmtp_MtpServer::remove_storage calling removeStorage\n"); server->removeStorage(storage); - delete storage; } } else MTPD("server is null in remove_storage"); + MTPD("twmtp_MtpServer::remove_storage DONE\n"); +} + +int twmtp_MtpServer::mtppipe_thread(void) +{ + if (mtp_read_pipe == -1) { + MTPD("mtppipe_thread exiting because mtp_read_pipe not set\n"); + return 0; + } + MTPD("Starting twmtp_MtpServer::mtppipe_thread\n"); + int read_count; + struct mtpmsg mtp_message; + while (1) { + read_count = ::read(mtp_read_pipe, &mtp_message, sizeof(mtp_message)); + MTPD("read %i from mtppipe\n", read_count); + if (read_count == sizeof(mtp_message)) { + if (mtp_message.message_type == MTP_MESSAGE_ADD_STORAGE) { + MTPI("mtppipe add storage %i '%s'\n", mtp_message.storage_id, mtp_message.path); + long reserveSpace = 1; + bool removable = false; + MtpStorage* storage = new MtpStorage(mtp_message.storage_id, mtp_message.path, mtp_message.display, reserveSpace, removable, mtp_message.maxFileSize, refserver); + server->addStorage(storage); + MTPD("mtppipe done adding storage\n"); + } else if (mtp_message.message_type == MTP_MESSAGE_REMOVE_STORAGE) { + MTPI("mtppipe remove storage %i\n", mtp_message.storage_id); + remove_storage(mtp_message.storage_id); + MTPD("mtppipe done removing storage\n"); + } else { + MTPE("Unknown mtppipe message value: %i\n", mtp_message.message_type); + } + } else { + MTPE("twmtp_MtpServer::mtppipe_thread unexpected read_count %i\n", read_count); + close(mtp_read_pipe); + break; + } + } + MTPD("twmtp_MtpServer::mtppipe_thread closing\n"); + return 0; +} + +void twmtp_MtpServer::set_read_pipe(int pipe) +{ + mtp_read_pipe = pipe; } diff --git a/mtp/mtp_MtpServer.hpp b/mtp/mtp_MtpServer.hpp index 3153e80bf..622ed6d6f 100755 --- a/mtp/mtp_MtpServer.hpp +++ b/mtp/mtp_MtpServer.hpp @@ -52,11 +52,16 @@ class twmtp_MtpServer { void add_storage(); void remove_storage(int storageId); void set_storages(storages* mtpstorages); + void set_read_pipe(int pipe); storages *stores; private: + typedef int (twmtp_MtpServer::*ThreadPtr)(void); + typedef void* (*PThreadPtr)(void *); + int mtppipe_thread(void); bool usePtp; MtpServer* server; MtpServer* refserver; + int mtp_read_pipe; }; #endif diff --git a/mtp/tw_sys_atomics.h b/mtp/tw_sys_atomics.h new file mode 100644 index 000000000..6349a931d --- /dev/null +++ b/mtp/tw_sys_atomics.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _TW_SYS_ATOMICS_H +#define _TW_SYS_ATOMICS_H + +#include +#include + +__BEGIN_DECLS + +/* Note: atomic operations that were exported by the C library didn't + * provide any memory barriers, which created potential issues on + * multi-core devices. We now define them as inlined calls to + * GCC sync builtins, which always provide a full barrier. + * + * NOTE: The C library still exports atomic functions by the same + * name to ensure ABI stability for existing NDK machine code. + * + * If you are an NDK developer, we encourage you to rebuild your + * unmodified sources against this header as soon as possible. + */ + +/* This was kanged from Android 4.4 bionic/libc/include/sys/atomics.h + * This header was removed in Android 5.0 in favor of stdatomics.h but + * to maintain compatibility across multiple trees, we are including our + * own copy. + */ + +#ifndef __ATOMIC_INLINE__ +#define __ATOMIC_INLINE__ static __inline__ __attribute__((always_inline)) +#endif + +__ATOMIC_INLINE__ int +__tw_atomic_cmpxchg(int old_value, int new_value, volatile int* ptr) +{ + /* We must return 0 on success */ + return __sync_val_compare_and_swap(ptr, old_value, new_value) != old_value; +} + +__END_DECLS + +#endif /* _TW_SYS_ATOMICS_H */ diff --git a/mtp/twrpMtp.cpp b/mtp/twrpMtp.cpp index d9db4246e..d47b8fa0d 100755 --- a/mtp/twrpMtp.cpp +++ b/mtp/twrpMtp.cpp @@ -72,12 +72,14 @@ twrpMtp::twrpMtp(int debug_enabled = 0) { if (debug_enabled) MtpDebug::enableDebug(); mtpstorages = new storages; + mtp_read_pipe = -1; } int twrpMtp::start(void) { MTPI("Starting MTP\n"); twmtp_MtpServer *mtp = new twmtp_MtpServer(); mtp->set_storages(mtpstorages); + mtp->set_read_pipe(mtp_read_pipe); mtp->start(); return 0; } @@ -90,7 +92,7 @@ pthread_t twrpMtp::threadserver(void) { return thread; } -pid_t twrpMtp::forkserver(void) { +pid_t twrpMtp::forkserver(int mtppipe[2]) { pid_t pid; if ((pid = fork()) == -1) { MTPE("MTP fork failed.\n"); @@ -98,8 +100,11 @@ pid_t twrpMtp::forkserver(void) { } if (pid == 0) { // Child process + close(mtppipe[1]); // Child closes write side + mtp_read_pipe = mtppipe[0]; start(); MTPD("MTP child process exited.\n"); + close(mtppipe[0]); _exit(0); } else { return pid; diff --git a/mtp/twrpMtp.hpp b/mtp/twrpMtp.hpp index 3aaa96414..ec7cd4b59 100755 --- a/mtp/twrpMtp.hpp +++ b/mtp/twrpMtp.hpp @@ -35,7 +35,7 @@ class twrpMtp { public: twrpMtp(int debug_enabled /* = 0 */); pthread_t threadserver(void); - pid_t forkserver(void); + pid_t forkserver(int mtppipe[2]); void addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize); private: int start(void); @@ -43,5 +43,6 @@ class twrpMtp { typedef void* (*PThreadPtr)(void *); storages *mtpstorages; storage *s; + int mtp_read_pipe; }; #endif diff --git a/partition.cpp b/partition.cpp index b3c436f8b..b20367f77 100644 --- a/partition.cpp +++ b/partition.cpp @@ -153,6 +153,7 @@ TWPartition::TWPartition() { Ignore_Blkid = false; Retain_Layout_Version = false; Crypto_Key_Location = "footer"; + MTP_Storage_ID = 0; } TWPartition::~TWPartition(void) { @@ -1028,7 +1029,7 @@ bool TWPartition::UnMount(bool Display_Error) { return true; // Never unmount system if you're not supposed to unmount it if (Is_Storage) - TWFunc::Toggle_MTP(false); + PartitionManager.Remove_MTP_Storage(MTP_Storage_ID); if (!Symlink_Mount_Point.empty()) umount(Symlink_Mount_Point.c_str()); @@ -1049,7 +1050,7 @@ bool TWPartition::UnMount(bool Display_Error) { } bool TWPartition::Wipe(string New_File_System) { - bool wiped = false, update_crypt = false, recreate_media = true, mtp_toggle = true; + bool wiped = false, update_crypt = false, recreate_media = true; int check; string Layout_Filename = Mount_Point + "/.layout_version"; @@ -1069,7 +1070,6 @@ bool TWPartition::Wipe(string New_File_System) { if (Has_Data_Media && Current_File_System == New_File_System) { wiped = Wipe_Data_Without_Wiping_Media(); recreate_media = false; - mtp_toggle = false; } else { DataManager::GetValue(TW_RM_RF_VAR, check); @@ -1088,9 +1088,6 @@ bool TWPartition::Wipe(string New_File_System) { else if (New_File_System == "f2fs") wiped = Wipe_F2FS(); else { - if (Is_Storage) { - TWFunc::Toggle_MTP(true); - } LOGERR("Unable to wipe '%s' -- unknown file system '%s'\n", Mount_Point.c_str(), New_File_System.c_str()); unlink("/.layout_version"); return false; @@ -1123,8 +1120,8 @@ bool TWPartition::Wipe(string New_File_System) { Recreate_Media_Folder(); } } - if (Is_Storage && mtp_toggle) { - TWFunc::Toggle_MTP(true); + if (Is_Storage) { + PartitionManager.Add_MTP_Storage(MTP_Storage_ID); } return wiped; } @@ -1377,20 +1374,22 @@ bool TWPartition::Wipe_Encryption() { Is_Decrypted = false; Is_Encrypted = false; Find_Actual_Block_Device(); - bool mtp_was_enabled = TWFunc::Toggle_MTP(false); if (Wipe(Fstab_File_System)) { Has_Data_Media = Save_Data_Media; if (Has_Data_Media && !Symlink_Mount_Point.empty()) { Recreate_Media_Folder(); + if (Mount(false)) + PartitionManager.Add_MTP_Storage(MTP_Storage_ID); } #ifndef TW_OEM_BUILD gui_print("You may need to reboot recovery to be able to use /data again.\n"); #endif - TWFunc::Toggle_MTP(mtp_was_enabled); return true; } else { Has_Data_Media = Save_Data_Media; LOGERR("Unable to format to remove encryption.\n"); + if (Has_Data_Media && Mount(false)) + PartitionManager.Add_MTP_Storage(MTP_Storage_ID); return false; } return false; @@ -1599,10 +1598,13 @@ bool TWPartition::Wipe_MTD() { } bool TWPartition::Wipe_RMRF() { - if (Is_Storage) - TWFunc::Toggle_MTP(false); if (!Mount(true)) return false; + // This is the only wipe that leaves the partition mounted, so we + // must manually remove the partition from MTP if it is a storage + // partition. + if (Is_Storage) + PartitionManager.Remove_MTP_Storage(MTP_Storage_ID); gui_print("Removing all files under '%s'\n", Mount_Point.c_str()); TWFunc::removeDir(Mount_Point, true); diff --git a/partitionmanager.cpp b/partitionmanager.cpp index 92b287557..ebd8c9675 100644 --- a/partitionmanager.cpp +++ b/partitionmanager.cpp @@ -43,6 +43,7 @@ #ifdef TW_HAS_MTP #include "mtp/mtp_MtpServer.hpp" #include "mtp/twrpMtp.hpp" +#include "mtp/MtpMessage.hpp" #endif extern "C" { @@ -57,6 +58,7 @@ extern bool datamedia; TWPartitionManager::TWPartitionManager(void) { mtp_was_enabled = false; + mtp_write_fd = -1; } int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) { @@ -64,6 +66,7 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) char fstab_line[MAX_FSTAB_LINE_LENGTH]; TWPartition* settings_partition = NULL; TWPartition* andsec_partition = NULL; + unsigned int storageid = 1 << 16; // upper 16 bits are for physical storage device, we pretend to have only one fstabFile = fopen(Fstab_Filename.c_str(), "rt"); if (fstabFile == NULL) { @@ -82,6 +85,10 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) memset(fstab_line, 0, sizeof(fstab_line)); if (partition->Process_Fstab_Line(line, Display_Error)) { + if (partition->Is_Storage) { + ++storageid; + partition->MTP_Storage_ID = storageid; + } if (!settings_partition && partition->Is_Settings_Storage && partition->Is_Present) { settings_partition = partition; } else { @@ -106,6 +113,9 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) datamedia = true; Dat->Setup_Data_Media(); settings_partition = Dat; + // Since /data was not considered a storage partition earlier, we still need to assign an MTP ID + ++storageid; + Dat->MTP_Storage_ID = storageid; } } if (!settings_partition) { @@ -301,6 +311,8 @@ void TWPartitionManager::Output_Partition(TWPartition* Part) { printf(" Backup_Method: %s\n", back_meth.c_str()); if (Part->Mount_Flags || !Part->Mount_Options.empty()) printf(" Mount_Flags=0x%8x, Mount_Options=%s\n", Part->Mount_Flags, Part->Mount_Options.c_str()); + if (Part->MTP_Storage_ID) + printf(" MTP_Storage_ID: %i\n", Part->MTP_Storage_ID); printf("\n"); } @@ -1146,13 +1158,10 @@ int TWPartitionManager::Wipe_Media_From_Data(void) { return false; gui_print("Wiping internal storage -- /data/media...\n"); - mtp_was_enabled = TWFunc::Toggle_MTP(false); + Remove_MTP_Storage(dat->MTP_Storage_ID); TWFunc::removeDir("/data/media", false); if (mkdir("/data/media", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0) { - if (mtp_was_enabled) { - if (!Enable_MTP()) - Disable_MTP(); - } + Add_MTP_Storage(dat->MTP_Storage_ID); return false; } if (dat->Has_Data_Media) { @@ -1161,10 +1170,7 @@ int TWPartitionManager::Wipe_Media_From_Data(void) { dat->UnMount(false); dat->Mount(false); } - if (mtp_was_enabled) { - if (!Enable_MTP()) - Disable_MTP(); - } + Add_MTP_Storage(dat->MTP_Storage_ID); return true; } else { LOGERR("Unable to locate /data.\n"); @@ -1485,7 +1491,7 @@ int TWPartitionManager::usb_storage_enable(void) { if (TWFunc::Path_Exists(lun_file)) has_multiple_lun = true; } - mtp_was_enabled = TWFunc::Toggle_MTP(false); + mtp_was_enabled = TWFunc::Toggle_MTP(false); // Must disable MTP for USB Storage if (!has_multiple_lun) { LOGINFO("Device doesn't have multiple lun files, mount current storage\n"); sprintf(lun_file, CUSTOM_LUN_FILE, 0); @@ -1896,6 +1902,14 @@ bool TWPartitionManager::Enable_MTP(void) { char vendor[PROPERTY_VALUE_MAX]; char product[PROPERTY_VALUE_MAX]; int count = 0; + + int mtppipe[2]; + + if (pipe(mtppipe) < 0) { + LOGERR("Error creating MTP pipe\n"); + return false; + } + property_set("sys.usb.config", "none"); property_get("usb.vendor", vendor, "18D1"); property_get("usb.product.mtpadb", product, "4EE2"); @@ -1910,24 +1924,29 @@ bool TWPartitionManager::Enable_MTP(void) { * twrp set tw_mtp_debug 1 */ twrpMtp *mtp = new twrpMtp(DataManager::GetIntValue("tw_mtp_debug")); - unsigned int storageid = 1 << 16; // upper 16 bits are for physical storage device, we pretend to have only one for (iter = Partitions.begin(); iter != Partitions.end(); iter++) { if ((*iter)->Is_Storage && (*iter)->Is_Present && (*iter)->Mount(false)) { - ++storageid; - printf("twrp addStorage %s, mtpstorageid: %u\n", (*iter)->Storage_Path.c_str(), storageid); - mtp->addStorage((*iter)->Storage_Name, (*iter)->Storage_Path, storageid, (*iter)->Get_Max_FileSize()); + printf("twrp addStorage %s, mtpstorageid: %u\n", (*iter)->Storage_Path.c_str(), (*iter)->MTP_Storage_ID); + mtp->addStorage((*iter)->Storage_Name, (*iter)->Storage_Path, (*iter)->MTP_Storage_ID, (*iter)->Get_Max_FileSize()); count++; } } if (count) { - mtppid = mtp->forkserver(); + mtppid = mtp->forkserver(mtppipe); if (mtppid) { + close(mtppipe[0]); // Host closes read side + mtp_write_fd = mtppipe[1]; DataManager::SetValue("tw_mtp_enabled", 1); return true; } else { + close(mtppipe[0]); + close(mtppipe[1]); LOGERR("Failed to enable MTP\n"); return false; } + } else { + close(mtppipe[0]); + close(mtppipe[1]); } LOGERR("No valid storage partitions found for MTP.\n"); #else @@ -1956,6 +1975,8 @@ bool TWPartitionManager::Disable_MTP(void) { mtppid = 0; // We don't care about the exit value, but this prevents a zombie process waitpid(mtppid, &status, 0); + close(mtp_write_fd); + mtp_write_fd = -1; } property_set("sys.usb.config", "adb"); DataManager::SetValue("tw_mtp_enabled", 0); @@ -1966,3 +1987,115 @@ bool TWPartitionManager::Disable_MTP(void) { return false; #endif } + +TWPartition* TWPartitionManager::Find_Partition_By_MTP_Storage_ID(unsigned int Storage_ID) { + std::vector::iterator iter; + + for (iter = Partitions.begin(); iter != Partitions.end(); iter++) { + if ((*iter)->MTP_Storage_ID == Storage_ID) + return (*iter); + } + return NULL; +} + +bool TWPartitionManager::Add_Remove_MTP_Storage(TWPartition* Part, int message_type) { +#ifdef TW_HAS_MTP + struct mtpmsg mtp_message; + + if (!mtppid) + return false; // MTP is disabled + + if (mtp_write_fd < 0) { + LOGERR("MTP: mtp_write_fd is not set\n"); + return false; + } + + if (Part) { + if (message_type == MTP_MESSAGE_REMOVE_STORAGE) { + mtp_message.message_type = MTP_MESSAGE_REMOVE_STORAGE; // Remove + LOGINFO("sending message to remove %i\n", Part->MTP_Storage_ID); + mtp_message.storage_id = Part->MTP_Storage_ID; + if (write(mtp_write_fd, &mtp_message, sizeof(mtp_message)) <= 0) { + LOGERR("error sending message to remove storage %i\n", Part->MTP_Storage_ID); + return false; + } else { + LOGINFO("Message sent, remove storage ID: %i\n", Part->MTP_Storage_ID); + return true; + } + } else if (message_type == MTP_MESSAGE_ADD_STORAGE && Part->Is_Mounted()) { + mtp_message.message_type = MTP_MESSAGE_ADD_STORAGE; // Add + mtp_message.storage_id = Part->MTP_Storage_ID; + mtp_message.path = Part->Storage_Path.c_str(); + mtp_message.display = Part->Storage_Name.c_str(); + mtp_message.maxFileSize = Part->Get_Max_FileSize(); + LOGINFO("sending message to add %i '%s'\n", Part->MTP_Storage_ID, mtp_message.path); + if (write(mtp_write_fd, &mtp_message, sizeof(mtp_message)) <= 0) { + LOGERR("error sending message to add storage %i\n", Part->MTP_Storage_ID); + return false; + } else { + LOGINFO("Message sent, add storage ID: %i\n", Part->MTP_Storage_ID); + return true; + } + } else { + LOGERR("Unknown MTP message type: %i\n", message_type); + } + } else { + // This hopefully never happens as the error handling should + // occur in the calling function. + LOGERR("TWPartitionManager::Add_Remove_MTP_Storage NULL partition given\n"); + } + return true; +#else + LOGERR("MTP support not included\n"); + DataManager::SetValue("tw_mtp_enabled", 0); + return false; +#endif +} + +bool TWPartitionManager::Add_MTP_Storage(string Mount_Point) { +#ifdef TW_HAS_MTP + TWPartition* Part = PartitionManager.Find_Partition_By_Path(Mount_Point); + if (Part) { + return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_ADD_STORAGE); + } else { + LOGERR("TWFunc::Add_MTP_Storage unable to locate partition for '%s'\n", Mount_Point.c_str()); + } +#endif + return false; +} + +bool TWPartitionManager::Add_MTP_Storage(unsigned int Storage_ID) { +#ifdef TW_HAS_MTP + TWPartition* Part = PartitionManager.Find_Partition_By_MTP_Storage_ID(Storage_ID); + if (Part) { + return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_ADD_STORAGE); + } else { + LOGERR("TWFunc::Add_MTP_Storage unable to locate partition for %i\n", Storage_ID); + } +#endif + return false; +} + +bool TWPartitionManager::Remove_MTP_Storage(string Mount_Point) { +#ifdef TW_HAS_MTP + TWPartition* Part = PartitionManager.Find_Partition_By_Path(Mount_Point); + if (Part) { + return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_REMOVE_STORAGE); + } else { + LOGERR("TWFunc::Remove_MTP_Storage unable to locate partition for '%s'\n", Mount_Point.c_str()); + } +#endif + return false; +} + +bool TWPartitionManager::Remove_MTP_Storage(unsigned int Storage_ID) { +#ifdef TW_HAS_MTP + TWPartition* Part = PartitionManager.Find_Partition_By_MTP_Storage_ID(Storage_ID); + if (Part) { + return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_REMOVE_STORAGE); + } else { + LOGERR("TWFunc::Remove_MTP_Storage unable to locate partition for %i\n", Storage_ID); + } +#endif + return false; +} diff --git a/partitions.hpp b/partitions.hpp index 8458e9363..43f553547 100644 --- a/partitions.hpp +++ b/partitions.hpp @@ -75,6 +75,7 @@ public: string MTD_Name; // Name of the partition for MTD devices bool Is_Present; // Indicates if the partition is currently present as a block device string Crypto_Key_Location; // Location of the crypto key used for decrypting encrypted data partitions + unsigned int MTP_Storage_ID; protected: bool Has_Data_Media; // Indicates presence of /data/media, may affect wiping and backup methods @@ -214,6 +215,10 @@ public: void Output_Storage_Fstab(); // Creates a /cache/recovery/storage.fstab file with a list of all potential storage locations for app use bool Enable_MTP(); // Enables MTP bool Disable_MTP(); // Disables MTP + bool Add_MTP_Storage(string Mount_Point); // Adds or removes an MTP Storage partition + bool Add_MTP_Storage(unsigned int Storage_ID); // Adds or removes an MTP Storage partition + bool Remove_MTP_Storage(string Mount_Point); // Adds or removes an MTP Storage partition + bool Remove_MTP_Storage(unsigned int Storage_ID); // Adds or removes an MTP Storage partition private: void Setup_Settings_Storage_Partition(TWPartition* Part); // Sets up settings storage @@ -222,10 +227,13 @@ private: bool Backup_Partition(TWPartition* Part, string Backup_Folder, bool generate_md5, unsigned long long* img_bytes_remaining, unsigned long long* file_bytes_remaining, unsigned long *img_time, unsigned long *file_time, unsigned long long *img_bytes, unsigned long long *file_bytes); bool Restore_Partition(TWPartition* Part, string Restore_Name, int partition_count, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); void Output_Partition(TWPartition* Part); + TWPartition* Find_Partition_By_MTP_Storage_ID(unsigned int Storage_ID); // Returns a pointer to a partition based on MTP Storage ID + bool Add_Remove_MTP_Storage(TWPartition* Part, int message_type); // Adds or removes an MTP Storage partition TWPartition* Find_Next_Storage(string Path, string Exclude); int Open_Lun_File(string Partition_Path, string Lun_File); pid_t mtppid; bool mtp_was_enabled; + int mtp_write_fd; private: std::vector Partitions; // Vector list of all partitions -- cgit v1.2.3