/* * 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++ */ #include "MtpDebug.h" #include "MtpStorage.h" #include "MtpDataPacket.h" #include "MtpServer.h" #include "MtpEventPacket.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY ) static int mtpid = 0; MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, const char* description, uint64_t reserveSpace, bool removable, uint64_t maxFileSize, MtpServer* refserver) : mStorageID(id), mFilePath(filePath), mDescription(description), mMaxCapacity(0), mMaxFileSize(maxFileSize), mReserveSpace(reserveSpace), mRemovable(removable), mServer(refserver) { MTPI("MtpStorage id: %d path: %s\n", id, filePath); mtpparentid = 0; inotify_thread = 0; sendEvents = false; use_mutex = true; if (pthread_mutex_init(&mtpMutex, NULL) != 0) { MTPE("Failed to init mtpMutex\n"); use_mutex = false; } if (pthread_mutex_init(&inMutex, NULL) != 0) { MTPE("Failed to init inMutex\n"); use_mutex = false; } } MtpStorage::~MtpStorage() { if (inotify_thread) { pthread_kill(inotify_thread, 0); for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { inotify_rm_watch(inotify_fd, i->first); } close(inotify_fd); } for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { delete i->second; } if (use_mutex) { use_mutex = false; pthread_mutex_destroy(&mtpMutex); pthread_mutex_destroy(&inMutex); } } int MtpStorage::getType() const { return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM); } int MtpStorage::getFileSystemType() const { return MTP_STORAGE_FILESYSTEM_HIERARCHICAL; } int MtpStorage::getAccessCapability() const { return MTP_STORAGE_READ_WRITE; } uint64_t MtpStorage::getMaxCapacity() { if (mMaxCapacity == 0) { struct statfs stat; if (statfs(getPath(), &stat)) return -1; mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize; } return mMaxCapacity; } uint64_t MtpStorage::getFreeSpace() { struct statfs stat; if (statfs(getPath(), &stat)) return -1; uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize; return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0); } const char* MtpStorage::getDescription() const { return (const char *)mDescription; } int MtpStorage::createDB() { std::string mtpParent = ""; mtpstorageparent = getPath(); readParentDirs(getPath()); while (!mtpParentList.empty()) { mtpParent = mtpParentList.front(); mtpParentList.pop_front(); readParentDirs(mtpParent); } MTPD("MtpStorage::createDB DONE\n"); if (use_mutex) { MTPD("Starting inotify thread\n"); sendEvents = true; inotify_thread = inotify(); } else { MTPD("NOT starting inotify thread\n"); } return 0; } MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) { std::vector mtpids; int local_mtpparentid; MTPD("MtpStorage::getObjectList\n"); MTPD("parent: %d\n", parent); //append object id (numerical #s) of database to int array MtpObjectHandleList* list = new MtpObjectHandleList(); if (parent == MTP_PARENT_ROOT) { MTPD("parent == MTP_PARENT_ROOT\n"); local_mtpparentid = 1; } else { for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { MTPD("root: %d\n", i->second->Root()); Node* node = i->second->findNode(parent, i->second->Root()); if (node != NULL) { local_mtpparentid = i->second->getMtpParentId(node); MTPD("path: %s\n", i->second->getPath(node).c_str()); MTPD("mtpparentid: %d going to endloop\n", local_mtpparentid); goto endloop; } } } MTPD("got to endloop\n"); endloop: if (mtpmap[local_mtpparentid] == NULL) { MTPD("mtpmap[mtpparentid] == NULL, returning\n"); return list; } MTPD("root: %d\n", mtpmap[local_mtpparentid]->Root()); mtpmap[local_mtpparentid]->getmtpids(mtpmap[local_mtpparentid]->Root(), &mtpids); MTPD("here, mtpids->size(): %i\n", mtpids.size()); for (unsigned index = 0; index < mtpids.size(); index++) { MTPD("mtpidhere[%i]: %d\n", index, mtpids.at(index)); list->push(mtpids.at(index)); } return list; } int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) { struct stat st; uint64_t size = 0; MTPD("MtpStorage::getObjectInfo handle: %d\n", handle); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { Node* node = i->second->findNode(handle, i->second->Root()); MTPD("node returned: %d\n", node); if (node != NULL) { MTPD("found mtpid: %d\n", node->Mtpid()); info.mStorageID = getStorageID(); MTPD("info.mStorageID: %d\n", info.mStorageID); info.mParent = node->getMtpParentId(); MTPD("mParent: %d\n", info.mParent); if (lstat(node->getPath().c_str(), &st) == 0) size = st.st_size; MTPD("size is: %llu\n", size); info.mCompressedSize = size;//(size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size); info.mDateModified = st.st_mtime; if (S_ISDIR(st.st_mode)) { info.mFormat = MTP_FORMAT_ASSOCIATION; } else { info.mFormat = MTP_FORMAT_UNDEFINED; } info.mName = strdup(basename(node->getPath().c_str())); MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n"); return 0; } } // Item is not on this storage device return -1; } MtpObjectHandle MtpStorage::beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified) { MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %d, storage: %d, format: %04x\n", path, parent, storage, format); Node* node; std::string parentdir; std::string pathstr(path); int parent_id; parentdir = pathstr.substr(0, pathstr.find_last_of('/')); MTPD("MtpStorage::beginSendObject() parentdir: %s\n", parentdir.c_str()); if (parentdir.compare(mtpstorageparent) == 0) { // root directory MTPD("MtpStorage::beginSendObject() root dir\n"); parent_id = 1; ++mtpid; node = mtpmap[parent_id]->addNode(mtpid, path); MTPD("node: %d\n", node); node->addProperties(storage, 0); if (format == MTP_FORMAT_ASSOCIATION) { createEmptyDir(path); } return mtpid; } else { for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { node = i->second->findNodePath(parentdir, i->second->Root()); if (node != NULL) { MTPD("mtpid: %d\n", mtpid); MTPD("path: %s\n", i->second->getPath(node).c_str()); parentdir = i->second->getPath(node); parent = i->second->getMtpParentId(node); if (parent == 0) { MTPD("MtpStorage::beginSendObject parent is 0, error.\n"); return -1; } else { ++mtpid; node = mtpmap[parent]->addNode(mtpid, path); node->addProperties(getStorageID(), getParentObject(parentdir)); for (iter i2 = mtpmap.begin(); i2 != mtpmap.end(); i2++) { node = i2->second->findNodePath(path, i2->second->Root()); if (node != NULL) { i2->second->setMtpParentId(parent, node); } } if (format == MTP_FORMAT_ASSOCIATION) { createEmptyDir(path); } } return mtpid; } } } MTPE("MtpStorage::beginSendObject(), path: '%s', parent: %d, storage: %d, format: %04x\n", path, parent, storage, format); return -1; } int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) { struct stat st; Node* node; MTPD("MtpStorage::getObjectFilePath handle: %i\n", handle); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("handle: %d\n", handle); node = i->second->findNode(handle, i->second->Root()); MTPD("node returned: %d\n", node); if (node != NULL) { if (lstat(node->getPath().c_str(), &st) == 0) outFileLength = st.st_size; else outFileLength = 0; outFilePath = strdup(node->getPath().c_str()); MTPD("outFilePath: %s\n", node->getPath().c_str()); goto end; } } // Item is not on this storage return -1; end: outFormat = MTP_FORMAT_ASSOCIATION; return 0; } int MtpStorage::readParentDirs(std::string path) { struct dirent *de; struct stat st; DIR *d; std::string parent, item, prevparent = ""; Node* node; int storageID = getStorageID(); d = opendir(path.c_str()); MTPD("opening '%s'\n", path.c_str()); if (d == NULL) { MTPD("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno)); closedir(d); } while ((de = readdir(d)) != NULL) { // Because exfat-fuse causes issues with dirent, we will use stat // for some things that dirent should be able to do item = path + "/" + de->d_name; if (lstat(item.c_str(), &st)) { MTPE("Error running lstat on '%s'\n", item.c_str()); return -1; } if ((st.st_mode & S_IFDIR) && strcmp(de->d_name, ".") == 0) continue; if ((st.st_mode & S_IFDIR) && strcmp(de->d_name, "..") != 0) { // Handle dirs MTPD("dir: %s\n", item.c_str()); mtpParentList.push_back(item); parent = item.substr(0, item.find_last_of('/')); ++mtpid; MTPD("parent: %s\n", parent.c_str()); MTPD("mtpid: %d\n", mtpid); if (prevparent != parent) { mtpparentid++; MTPD("Handle dirs, prevparent != parent, mtpparentid: %d\n", mtpparentid); mtpmap[mtpparentid] = new Tree(); MTPD("prevparent addNode\n"); for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(parent, i->second->Root()); if (node != NULL) { i->second->setMtpParentId(mtpparentid, node); } } node = mtpmap[mtpparentid]->addNode(mtpid, item); node->addProperties(storageID, getParentObject(path)); if (sendEvents) mServer->sendObjectAdded(mtpid); } else { MTPD("add node\n"); mtpmap[mtpparentid]->addNode(mtpid, item); for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(item, i->second->Root()); if (node != NULL) { i->second->setMtpParentId(mtpparentid, node); node->addProperties(storageID, getParentObject(path)); } } if (sendEvents) mServer->sendObjectAdded(mtpid); } prevparent = parent; } else { if (strcmp(de->d_name, "..") != 0) { // Handle files item = path + "/" + de->d_name; MTPD("file: %s\n", item.c_str()); parent = item.substr(0, item.find_last_of('/')); MTPD("parent: %s\n", parent.c_str()); ++mtpid; MTPD("mtpid: %d\n", mtpid); if (prevparent != parent) { mtpparentid++; MTPD("mtpparentid1: %d\n", mtpparentid); for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(path, i->second->Root()); if (node != NULL) { i->second->setMtpParentId(mtpparentid, node); node->addProperties(storageID, getParentObject(path)); } } } if (mtpmap[mtpparentid] == NULL) { mtpmap[mtpparentid] = new Tree(); } MTPD("blank addNode\n"); node = mtpmap[mtpparentid]->addNode(mtpid, item); node->addProperties(storageID, getParentObject(path)); prevparent = parent; if (sendEvents) mServer->sendObjectAdded(mtpid); } else { // Handle empty dirs? MTPD("checking for empty dir '%s'\n", path.c_str()); int count = 0; DIR *dirc; struct dirent *ep; dirc = opendir(path.c_str()); if (dirc != NULL) { while ((ep = readdir(dirc))) ++count; MTPD("count: %d\n", count); closedir(dirc); } if (count == 2) { MTPD("'%s' is an empty dir\n", path.c_str()); createEmptyDir(path.c_str()); goto end; } } } } end: closedir(d); return 0; } void MtpStorage::deleteTrees(int parent) { Node* node = mtpmap[parent]->Root(); MTPD("MtpStorage::deleteTrees deleting %i\n", parent); while (node != NULL) { if (node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT) == MTP_FORMAT_ASSOCIATION) { deleteTrees(node->getMtpParentId()); } node = mtpmap[parent]->getNext(node); } delete mtpmap[parent]; mtpmap.erase(parent); MTPD("MtpStorage::deleteTrees deleted %i\n", parent); } int MtpStorage::deleteFile(MtpObjectHandle handle) { int local_parent_id = 0; for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("MtpStorage::deleteFile handle: %d\n", handle); Node* node = i->second->findNode(handle, i->second->Root()); MTPD("MtpStorage::deleteFile node returned: %d\n", node); if (node != NULL) { if (node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT) == MTP_FORMAT_ASSOCIATION) { local_parent_id = node->getMtpParentId(); } MTPD("deleting handle: %d\n", handle); i->second->deleteNode(handle); MTPD("deleted\n"); goto end; } } return -1; end: if (local_parent_id) { deleteTrees(local_parent_id); } return 0; } int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) { Node *n; int local_mtpid = 0; int local_mtpparentid = 0; std::vector propertyCodes; std::vector dataTypes; std::vector valueStrs; std::vector longValues; int count = 0; MTPD("MtpStorage::getObjectPropertyList handle: %d, format: %d, property: %lx\n", handle, format, property); if (property == MTP_PROPERTY_OBJECT_FORMAT) { MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_OBJECT_FORMAT\n"); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("root: %d\n", i->second->Root()); Node *node = i->second->findNode(handle, i->second->Root()); MTPD("index: %d\n", index); MTPD("node: %d\n", node); if (node != NULL) { uint64_t longval = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT); local_mtpparentid = i->second->getMtpParentId(node); MTPD("object format longValue: %llu\n", longval); propertyCodes.push_back(MTP_PROPERTY_OBJECT_FORMAT); longValues.push_back(node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT)); valueStrs.push_back(""); dataTypes.push_back(4); count = 1; local_mtpid = node->Mtpid(); goto endloop; } } } else if (property == MTP_PROPERTY_STORAGE_ID) { MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_STORAGE_ID\n"); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("root: %d\n", i->second->Root()); Node *node = i->second->findNode(handle, i->second->Root()); if (node != NULL) { propertyCodes.push_back(MTP_PROPERTY_STORAGE_ID); longValues.push_back(getStorageID()); valueStrs.push_back(""); dataTypes.push_back(4); count = 1; local_mtpid = node->Mtpid(); goto endloop; } } } else if (property == MTP_PARENT_ROOT) { MTPD("MtpStorage::getObjectPropertyList MTP_PARENT_ROOT\n"); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("root: %d\n", i->second->Root()); Node* node = i->second->findNode(handle, i->second->Root()); if (node != NULL) { local_mtpparentid = i->second->getMtpParentId(node); MTPD("path: %s\n", i->second->getPath(node).c_str()); MTPD("mtpparentid: %d going to endloop\n", local_mtpparentid); std::vector mtpprop = node->getMtpProps(); count = mtpprop.size(); for (int i = 0; i < count; ++i) { propertyCodes.push_back(mtpprop[i].property); longValues.push_back(mtpprop[i].valueInt); valueStrs.push_back(mtpprop[i].valueStr); dataTypes.push_back(mtpprop[i].dataType); } local_mtpid = node->Mtpid(); goto endloop; } } } else if (property == MTP_PROPERTY_PROTECTION_STATUS) { MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_PROTECTION_STATUS\n"); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("root: %d\n", i->second->Root()); Node *node = i->second->findNode(handle, i->second->Root()); if (node != NULL) { propertyCodes.push_back(MTP_PROPERTY_PROTECTION_STATUS); longValues.push_back(0); valueStrs.push_back(""); dataTypes.push_back(8); count = 1; local_mtpid = node->Mtpid(); goto endloop; } } } else if (property == MTP_PROPERTY_OBJECT_SIZE) { MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_OBJECT_SIZE\n"); for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("root: %d\n", i->second->Root()); Node *node = i->second->findNode(handle, i->second->Root()); if (node != NULL) { struct stat st; uint64_t size = 0; if (lstat(node->getPath().c_str(), &st) == 0) size = st.st_size; propertyCodes.push_back(MTP_PROPERTY_OBJECT_SIZE); longValues.push_back(size); valueStrs.push_back(""); dataTypes.push_back(8); count = 1; local_mtpid = node->Mtpid(); goto endloop; } } } else { // Either the property is not supported or the handle is not on this storage return -1; } // handle not found on this storage return -1; endloop: MTPD("mtpparentid: %d\n", local_mtpparentid); MTPD("count: %d\n", count); packet.putUInt32(count); if (count > 0) { std::string stringValuesArray; for (int i = 0; i < count; ++i) { packet.putUInt32(local_mtpid); packet.putUInt16(propertyCodes[i]); MTPD("dataTypes: %d\n", dataTypes[i]); packet.putUInt16(dataTypes[i]); MTPD("propertyCode: %s\n", MtpDebug::getObjectPropCodeName(propertyCodes[i])); MTPD("longValues: %d\n", longValues[i]); switch (dataTypes[i]) { case MTP_TYPE_INT8: MTPD("MTP_TYPE_INT8\n"); packet.putInt8(longValues[i]); break; case MTP_TYPE_UINT8: MTPD("MTP_TYPE_UINT8\n"); packet.putUInt8(longValues[i]); break; case MTP_TYPE_INT16: MTPD("MTP_TYPE_INT16\n"); packet.putInt16(longValues[i]); break; case MTP_TYPE_UINT16: MTPD("MTP_TYPE_UINT16\n"); packet.putUInt16(longValues[i]); break; case MTP_TYPE_INT32: MTPD("MTP_TYPE_INT32\n"); packet.putInt32(longValues[i]); break; case MTP_TYPE_UINT32: MTPD("MTP_TYPE_UINT32\n"); packet.putUInt32(longValues[i]); break; case MTP_TYPE_INT64: MTPD("MTP_TYPE_INT64\n"); packet.putInt64(longValues[i]); break; case MTP_TYPE_UINT64: MTPD("MTP_TYPE_UINT64\n"); packet.putUInt64(longValues[i]); break; case MTP_TYPE_INT128: MTPD("MTP_TYPE_INT128\n"); packet.putInt128(longValues[i]); break; case MTP_TYPE_UINT128: MTPD("MTP_TYPE_UINT128\n"); packet.putUInt128(longValues[i]); break; case MTP_TYPE_STR: MTPD("MTP_TYPE_STR: %s\n", valueStrs[i].c_str()); packet.putString((const char*) valueStrs[i].c_str()); break; default: MTPE("bad or unsupported data type: %i in MyMtpDatabase::getObjectPropertyList", dataTypes[i]); break; } } } return 0; } int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) { int index; MTPD("MtpStorage::renameObject, handle: %d, new name: '%s'\n", handle, newName.c_str()); if (handle == MTP_PARENT_ROOT) { MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n"); return -1; } else { for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { MTPD("root: %d\n", i->second->Root()); Node* node = i->second->findNode(handle, i->second->Root()); if (node != NULL) { std::string oldName = i->second->getPath(node); std::string parentdir = oldName.substr(0, oldName.find_last_of('/')); std::string newFullName = parentdir + "/" + newName; MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str()); if (rename(oldName.c_str(), newFullName.c_str()) == 0) { node->rename(newFullName); return 0; } else { MTPE("MtpStorage::renameObject failed, handle: %d, new name: '%s'\n", handle, newName.c_str()); return -1; } } } } // handle not found on this storage return -1; } void MtpStorage::createEmptyDir(const char* path) { Node *node; ++mtpparentid; MtpStorageID storage = getStorageID(); MTPD("MtpStorage::createEmptyDir path: '%s', storage: %i, mtpparentid: %d\n", path, storage, mtpparentid); mtpmap[mtpparentid] = new Tree(); for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(path, i->second->Root()); if (node != NULL) { mtpmap[mtpparentid]->setMtpParentId(mtpparentid, node); } } } int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, uint64_t &longValue) { Node *node; for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { node = i->second->findNode(handle, i->second->Root()); if (node != NULL) { longValue = node->getIntProperty(property); return 0; } } // handle not found on this storage return -1; } pthread_t MtpStorage::inotify(void) { pthread_t thread; ThreadPtr inotifyptr = &MtpStorage::inotify_t; PThreadPtr p = *(PThreadPtr*)&inotifyptr; pthread_create(&thread, NULL, p, this); return thread; } int MtpStorage::addInotifyDirs(std::string path) { struct dirent *de; DIR *d; struct stat st; std::string inotifypath; d = opendir(path.c_str()); if (d == NULL) { MTPE("MtpStorage::addInotifyDirs unable to open '%s'\n", path.c_str()); closedir(d); return -1; } while ((de = readdir(d)) != NULL) { inotifypath = path + "/" + de->d_name; if (lstat(inotifypath.c_str(), &st)) { MTPE("Error using lstat on '%s'\n", inotifypath.c_str()); return -1; } if (!(st.st_mode & S_IFDIR) || strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; if (addInotifyDirs(inotifypath)) { closedir(d); return -1; } inotify_wd = inotify_add_watch(inotify_fd, inotifypath.c_str(), WATCH_FLAGS); inotifymap[inotify_wd] = inotifypath; MTPD("added inotify dir: '%s'\n", inotifypath.c_str()); } closedir(d); return 0; } int MtpStorage::inotify_t(void) { int len, i = 0; int local_mtpparentid; Node* node = NULL; struct stat st; #define EVENT_SIZE ( sizeof(struct inotify_event) ) #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) ) char buf[EVENT_BUF_LEN]; std::string item, parent = ""; MTPD("starting inotify thread\n"); inotify_fd = inotify_init(); if (inotify_fd < 0){ MTPE("Can't run inotify for mtp server\n"); } inotify_wd = inotify_add_watch(inotify_fd, getPath(), WATCH_FLAGS); inotifymap[inotify_wd] = getPath(); if (addInotifyDirs(getPath())) { MTPE("MtpStorage::inotify_t failed to add watches to directories\n"); for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { inotify_rm_watch(inotify_fd, i->first); } close(inotify_fd); return -1; } while (true) { i = 0; len = read(inotify_fd, buf, EVENT_BUF_LEN); if (len < 0) { MTPE("inotify_t Can't read inotify events\n"); } while (i < len) { struct inotify_event *event = ( struct inotify_event * ) &buf[ i ]; if ( event->len ) { if (inotifymap[event->wd].empty()) { MTPE("Unable to locate inotify_wd: %i\n", event->wd); goto end; } else { item = inotifymap[event->wd]; item = item + "/" + event->name; MTPD("inotify_t item: '%s'\n", item.c_str()); if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { lockMutex(1); if (event->mask & IN_ISDIR) { MTPD("inotify_t create is dir\n"); } else { MTPD("inotify_t create is file\n"); } for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(item, i->second->Root()); if (node != NULL) break; } if (node == NULL) { parent = item.substr(0, item.find_last_of('/')); MTPD("parent: %s\n", parent.c_str()); if (parent == getPath()) { local_mtpparentid = 1; } else { for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(parent, i->second->Root()); MTPD("searching for node: %d\n", (int)node); if (node != NULL) { local_mtpparentid = i->second->getMtpParentId(node); break; } } if (node == NULL) { MTPE("inotify_t unable to locate mtparentid\n"); goto end; } } ++mtpid; MTPD("mtpid: %d\n", mtpid); MTPD("mtpparentid1: %d\n", local_mtpparentid); node = mtpmap[local_mtpparentid]->addNode(mtpid, item); mtpmap[local_mtpparentid]->setMtpParentId(local_mtpparentid, node); node->addProperties(getStorageID(), getParentObject(parent)); if (event->mask & IN_ISDIR) { createEmptyDir(item.c_str()); } mServer->sendObjectAdded(mtpid); } else { MTPD("inotify_t item already exists.\n"); } if (event->mask & IN_ISDIR) { inotify_wd = inotify_add_watch(inotify_fd, item.c_str(), WATCH_FLAGS); inotifymap[inotify_wd] = item; MTPD("added inotify dir: '%s'\n", item.c_str()); MTPD("inotify_t scanning new dir\n"); readParentDirs(item); std::string mtpParent; while (!mtpParentList.empty()) { mtpParent = mtpParentList.front(); mtpParentList.pop_front(); readParentDirs(mtpParent); inotify_wd = inotify_add_watch(inotify_fd, mtpParent.c_str(), WATCH_FLAGS); inotifymap[inotify_wd] = mtpParent; MTPD("added inotify dir: '%s'\n", mtpParent.c_str()); } } } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { lockMutex(1); if (event->mask & IN_ISDIR) { MTPD("inotify_t Directory %s deleted\n", event->name); } else { MTPD("inotify_t File %s deleted\n", event->name); } for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(item, i->second->Root()); if (node != NULL) break; } if (node != NULL && node->Mtpid() > 0) { int local_id = node->Mtpid(); node = NULL; deleteFile(local_id); mServer->sendObjectRemoved(local_id); } else { MTPD("inotify_t already removed.\n"); } if (event->mask & IN_ISDIR) { std::string orig_item = item + "/"; size_t item_size = orig_item.size(); std::string path_check; for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { if ((i->second.size() > item_size && i->second.substr(0, item_size) == orig_item) || i->second == item) { inotify_rm_watch(inotify_fd, i->first); MTPD("inotify_t removing watch on '%s'\n", i->second.c_str()); inotifymap.erase(i->first); } } } } else if (event->mask & IN_MODIFY) { MTPD("inotify_t item %s modified.\n", event->name); for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(item, i->second->Root()); if (node != NULL) break; } if (node != NULL) { uint64_t orig_size = node->getIntProperty(MTP_PROPERTY_OBJECT_SIZE); struct stat st; uint64_t new_size = 0; if (lstat(item.c_str(), &st) == 0) new_size = (uint64_t)st.st_size; if (orig_size != new_size) { MTPD("size changed from %llu to %llu on mtpid: %i\n", orig_size, new_size, node->Mtpid()); node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64); mServer->sendObjectUpdated(node->Mtpid()); } } else { MTPE("inotify_t modified item not found\n"); } } } } end: unlockMutex(1); i += EVENT_SIZE + event->len; } } for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { inotify_rm_watch(inotify_fd, i->first); } close(inotify_fd); return 0; } int MtpStorage::getParentObject(std::string parent_path) { Node* node; if (parent_path == getPath()) { MTPD("MtpStorage::getParentObject for: '%s' returning: 0 for root\n", parent_path.c_str()); return 0; } for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { node = i->second->findNodePath(parent_path, i->second->Root()); if (node != NULL) { MTPD("MtpStorage::getParentObject for: '%s' returning: %i\n", parent_path.c_str(), node->Mtpid()); return node->Mtpid(); } } MTPE("MtpStorage::getParentObject for: '%s' unable to locate node\n", parent_path.c_str()); return -1; } void MtpStorage::lockMutex(int thread_type) { if (!use_mutex) return; // mutex is disabled if (thread_type) { // inotify thread pthread_mutex_lock(&inMutex); while (pthread_mutex_trylock(&mtpMutex)) { pthread_mutex_unlock(&inMutex); usleep(32000); pthread_mutex_lock(&inMutex); } } else { // main mtp thread pthread_mutex_lock(&mtpMutex); while (pthread_mutex_trylock(&inMutex)) { pthread_mutex_unlock(&mtpMutex); usleep(13000); pthread_mutex_lock(&mtpMutex); } } } void MtpStorage::unlockMutex(int thread_type) { if (!use_mutex) return; // mutex is disabled pthread_mutex_unlock(&inMutex); pthread_mutex_unlock(&mtpMutex); }