From 9e0593eaf6defb15761f41246093c0d3661b140d Mon Sep 17 00:00:00 2001 From: that Date: Wed, 8 Oct 2014 00:01:24 +0200 Subject: mtp: cleanup, fixes and performance improvements - use std::map instead of linked list - read directories on demand - fix writing zip files to storage root - fix creating directories - lots of minor fixes - simplify generation of storage IDs and make them spec compliant Change-Id: I2137c27549ddbdc58466f2e3aeda464fac70a3c5 --- mtp/MtpStorage.cpp | 1152 +++++++++++++++++++++-------------------------- mtp/MtpStorage.h | 43 +- mtp/btree.cpp | 289 ++---------- mtp/btree.hpp | 94 ++-- mtp/mtp_MtpDatabase.cpp | 103 ++--- mtp/node.cpp | 88 ++-- 6 files changed, 691 insertions(+), 1078 deletions(-) (limited to 'mtp') diff --git a/mtp/MtpStorage.cpp b/mtp/MtpStorage.cpp index 57c87745e..4c55361fa 100755 --- a/mtp/MtpStorage.cpp +++ b/mtp/MtpStorage.cpp @@ -21,6 +21,7 @@ #include "MtpDataPacket.h" #include "MtpServer.h" #include "MtpEventPacket.h" +#include "MtpDatabase.h" #include #include @@ -35,12 +36,9 @@ #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) @@ -54,9 +52,9 @@ MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, mServer(refserver) { MTPI("MtpStorage id: %d path: %s\n", id, filePath); - mtpparentid = 0; inotify_thread = 0; sendEvents = false; + handleCurrentlySending = 0; use_mutex = true; if (pthread_mutex_init(&mtpMutex, NULL) != 0) { MTPE("Failed to init mtpMutex\n"); @@ -71,8 +69,9 @@ MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, MtpStorage::~MtpStorage() { if (inotify_thread) { + // TODO: what does this do? manpage says it does not kill the thread pthread_kill(inotify_thread, 0); - for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { + for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { inotify_rm_watch(inotify_fd, i->first); } close(inotify_fd); @@ -124,12 +123,8 @@ const char* MtpStorage::getDescription() const { int MtpStorage::createDB() { std::string mtpParent = ""; mtpstorageparent = getPath(); - readParentDirs(getPath()); - while (!mtpParentList.empty()) { - mtpParent = mtpParentList.front(); - mtpParentList.pop_front(); - readParentDirs(mtpParent); - } + // root directory is special: handle 0, parent 0, and empty path + mtpmap[0] = new Tree(0, 0, ""); MTPD("MtpStorage::createDB DONE\n"); if (use_mutex) { MTPD("Starting inotify thread\n"); @@ -138,524 +133,399 @@ int MtpStorage::createDB() { } else { MTPD("NOT starting inotify thread\n"); } + // for debugging and caching purposes, read the root dir already now + readDir(mtpstorageparent, mtpmap[0]); + // all other dirs are read on demand return 0; } MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) { - std::vector mtpids; - int local_mtpparentid; - MTPD("MtpStorage::getObjectList\n"); - MTPD("parent: %d\n", parent); + MTPD("MtpStorage::getObjectList, parent: %u\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; - } - } + parent = 0; } - MTPD("got to endloop\n"); - endloop: - if (mtpmap[local_mtpparentid] == NULL) { - MTPD("mtpmap[mtpparentid] == NULL, returning\n"); + if (mtpmap.find(parent) == mtpmap.end()) { + MTPE("parent handle not found, returning empty list\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)); + Tree* tree = mtpmap[parent]; + if (!tree->wasAlreadyRead()) + { + std::string path = getNodePath(tree); + MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str()); + readDir(path, tree); } + + mtpmap[parent]->getmtpids(list); + MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str()); 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; - } + MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle); + Node* node = findNode(handle); + if (!node) { + // Item is not on this storage device + return -1; } - // Item is not on this storage device - return -1; + + info.mStorageID = getStorageID(); + MTPD("info.mStorageID: %u\n", info.mStorageID); + info.mParent = node->getMtpParentId(); + MTPD("mParent: %u\n", info.mParent); + // TODO: do we want to lstat again here, or read from the node properties? + if (lstat(getNodePath(node).c_str(), &st) == 0) + size = st.st_size; + MTPD("size is: %llu\n", size); + info.mCompressedSize = (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(node->getName().c_str()); + MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n"); + return 0; } 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; + MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format); + iter it = mtpmap.find(parent); + if (it == mtpmap.end()) { + MTPE("parent node not found, returning error\n"); + return kInvalidObjectHandle; + } + Tree* tree = it->second; + 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; - } - } + size_t slashpos = pathstr.find_last_of('/'); + if (slashpos == std::string::npos) { + MTPE("path has no slash, returning error\n"); + return kInvalidObjectHandle; } - MTPE("MtpStorage::beginSendObject(), path: '%s', parent: %d, storage: %d, format: %04x\n", path, parent, storage, format); - return -1; + std::string parentdir = pathstr.substr(0, slashpos); + std::string basename = pathstr.substr(slashpos + 1); + if (parent != 0 && parentdir != getNodePath(tree)) { + MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str()); + return kInvalidObjectHandle; + } + + MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str()); + // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle + bool isDir = format == MTP_FORMAT_ASSOCIATION; + Node* node = addNewNode(isDir, tree, basename); + handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending + + return node->Mtpid(); +} + +void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded) +{ + Node* node = findNode(handle); + if (!node) + return; // just ignore if this is for another storage + + node->addProperties(path, mStorageID); + handleCurrentlySending = 0; + // TODO: are we supposed to send an event about an upload by the initiator? + if (sendEvents) + mServer->sendObjectAdded(node->Mtpid()); } 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; - } + MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle); + Node* node = findNode(handle); + if (!node) + { + // Item is not on this storage device + return -1; } - // Item is not on this storage - return -1; -end: - outFormat = MTP_FORMAT_ASSOCIATION; + // TODO: do we want to lstat here, or just read the info from the node? + struct stat st; + if (lstat(getNodePath(node).c_str(), &st) == 0) + outFileLength = st.st_size; + else + outFileLength = 0; + outFilePath = getNodePath(node).c_str(); + MTPD("outFilePath: %s\n", outFilePath.string()); + outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED; return 0; } -int MtpStorage::readParentDirs(std::string path) { +int MtpStorage::readDir(const std::string& path, Tree* tree) +{ struct dirent *de; - struct stat st; - DIR *d; - std::string parent, item, prevparent = ""; - Node* node; int storageID = getStorageID(); + MtpObjectHandle parent = tree->Mtpid(); - d = opendir(path.c_str()); - MTPD("opening '%s'\n", path.c_str()); + DIR *d = opendir(path.c_str()); + MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent); if (d == NULL) { - MTPD("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno)); - closedir(d); + MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno)); + return -1; } + // TODO: for refreshing dirs: capture old entries here 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; + std::string item = path + "/" + de->d_name; + struct stat st; 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) + // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite + if (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); + if (strcmp(de->d_name, "..") == 0) + continue; + Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name); + node->addProperties(item, storageID); + //if (sendEvents) + // mServer->sendObjectAdded(node->Mtpid()); + // sending events here makes simple-mtpfs very slow, and it is probably the wrong thing to do anyway } - delete mtpmap[parent]; - mtpmap.erase(parent); - MTPD("MtpStorage::deleteTrees deleted %i\n", parent); + closedir(d); + // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes) + tree->setAlreadyRead(true); + addInotify(tree); + return 0; } 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; - } + MTPD("MtpStorage::deleteFile handle: %u\n", handle); + Node* node = findNode(handle); + if (!node) { + // Item is not on this storage device + return -1; } - return -1; -end: - if (local_parent_id) { - deleteTrees(local_parent_id); + MtpObjectHandle parent = node->getMtpParentId(); + Tree* tree = mtpmap[parent]; + if (!tree) { + MTPE("parent tree for handle %u not found\n", parent); + return -1; + } + if (node->isDir()) { + MTPD("deleting tree from mtpmap: %u\n", handle); + mtpmap.erase(handle); } + + MTPD("deleting handle: %u\n", handle); + tree->deleteNode(handle); + MTPD("deleted\n"); 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; - } +void MtpStorage::queryNodeProperties(std::vector& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID) +{ + MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str()); + PropEntry pe; + pe.handle = node->Mtpid(); + pe.property = property; + + if (property == 0xffffffff) + { + // add all properties + MTPD("MtpStorage::queryNodeProperties for all properties\n"); + std::vector mtpprop = node->getMtpProps(); + for (size_t i = 0; i < mtpprop.size(); ++i) { + pe.property = mtpprop[i].property; + pe.datatype = mtpprop[i].dataType; + pe.intvalue = mtpprop[i].valueInt; + pe.strvalue = mtpprop[i].valueStr; + results.push_back(pe); } - } - 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; - } + return; + } + else if (property == 0) + { + // TODO: use groupCode + } + + // single property + // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue + switch (property) { +// case MTP_PROPERTY_OBJECT_FORMAT: +// pe.datatype = MTP_TYPE_UINT16; +// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT); +// break; + + case MTP_PROPERTY_STORAGE_ID: + pe.datatype = MTP_TYPE_UINT32; + pe.intvalue = storageID; + break; + + case MTP_PROPERTY_PROTECTION_STATUS: + pe.datatype = MTP_TYPE_UINT16; + pe.intvalue = 0; + break; + + case MTP_PROPERTY_OBJECT_SIZE: + { + pe.datatype = MTP_TYPE_UINT64; + struct stat st; + pe.intvalue = 0; + if (lstat(getNodePath(node).c_str(), &st) == 0) + pe.intvalue = st.st_size; + break; } - } - 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; + + default: + { + const Node::mtpProperty& prop = node->getProperty(property); + if (prop.property != property) + { + MTPD("queryNodeProperties: unknown property %x\n", property); + return; } + pe.datatype = prop.dataType; + pe.intvalue = prop.valueInt; + pe.strvalue = prop.valueStr; + // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here } + } - 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; + results.push_back(pe); +} + +int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) { + MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property); + if (groupCode != 0) + { + MTPE("getObjectPropertyList: groupCode unsupported\n"); + return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED + } + // TODO: support all the special stuff, like: + // handle == 0 -> all objects at the root level + // handle == 0xffffffff -> all objects (on all storages? how could we support that?) + // format == 0 -> all formats, otherwise filter by ObjectFormatCode + // property == 0xffffffff -> all properties except those with group code 0xffffffff + // if property == 0 then use groupCode + // groupCode == 0 -> return Specification_By_Group_Unsupported + // depth == 0xffffffff -> all objects incl. and below handle + + std::vector results; + + if (handle == 0xffffffff) { + // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage) + } else if (handle == 0) { + // all objects at the root level + Tree* root = mtpmap[0]; + MtpObjectHandleList list; + root->getmtpids(&list); + for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) { + Node* node = root->findNode(*it); + if (!node) { + MTPE("BUG: node not found for root entry with handle %u\n", *it); + break; } + queryNodeProperties(results, node, property, groupCode, mStorageID); } - } - 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 { + // single object + Node* node = findNode(handle); + if (!node) { + // Item is not on this storage device + return -1; } - } - 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; - } + queryNodeProperties(results, node, property, groupCode, mStorageID); + } + + MTPD("count: %u\n", results.size()); + packet.putUInt32(results.size()); + + for (size_t i = 0; i < results.size(); ++i) { + PropEntry& p = results[i]; + MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n", + p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property), + p.datatype, p.intvalue); + packet.putUInt32(p.handle); + packet.putUInt16(p.property); + packet.putUInt16(p.datatype); + switch (p.datatype) { + case MTP_TYPE_INT8: + MTPD("MTP_TYPE_INT8\n"); + packet.putInt8(p.intvalue); + break; + case MTP_TYPE_UINT8: + MTPD("MTP_TYPE_UINT8\n"); + packet.putUInt8(p.intvalue); + break; + case MTP_TYPE_INT16: + MTPD("MTP_TYPE_INT16\n"); + packet.putInt16(p.intvalue); + break; + case MTP_TYPE_UINT16: + MTPD("MTP_TYPE_UINT16\n"); + packet.putUInt16(p.intvalue); + break; + case MTP_TYPE_INT32: + MTPD("MTP_TYPE_INT32\n"); + packet.putInt32(p.intvalue); + break; + case MTP_TYPE_UINT32: + MTPD("MTP_TYPE_UINT32\n"); + packet.putUInt32(p.intvalue); + break; + case MTP_TYPE_INT64: + MTPD("MTP_TYPE_INT64\n"); + packet.putInt64(p.intvalue); + break; + case MTP_TYPE_UINT64: + MTPD("MTP_TYPE_UINT64\n"); + packet.putUInt64(p.intvalue); + break; + case MTP_TYPE_INT128: + MTPD("MTP_TYPE_INT128\n"); + packet.putInt128(p.intvalue); + break; + case MTP_TYPE_UINT128: + MTPD("MTP_TYPE_UINT128\n"); + packet.putUInt128(p.intvalue); + break; + case MTP_TYPE_STR: + MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str()); + packet.putString(p.strvalue.c_str()); + break; + default: + MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype); + 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()); + MTPD("MtpStorage::renameObject, handle: %u, 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()); + Node* node = i->second->findNode(handle); if (node != NULL) { - std::string oldName = i->second->getPath(node); + std::string oldName = getNodePath(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); + node->rename(newName); return 0; } else { - MTPE("MtpStorage::renameObject failed, handle: %d, new name: '%s'\n", handle, newName.c_str()); + MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str()); return -1; } } @@ -665,32 +535,28 @@ int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) { 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) { +int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) { Node *node; for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { - node = i->second->findNode(handle, i->second->Root()); + node = i->second->findNode(handle); if (node != NULL) { - longValue = node->getIntProperty(property); + const Node::mtpProperty& prop = node->getProperty(property); + if (prop.property != property) { + MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle); + return -1; + } + pe.datatype = prop.dataType; + pe.intvalue = prop.valueInt; + pe.strvalue = prop.valueStr; + pe.handle = handle; + pe.property = property; return 0; } } // handle not found on this storage return -1; } + pthread_t MtpStorage::inotify(void) { pthread_t thread; ThreadPtr inotifyptr = &MtpStorage::inotify_t; @@ -699,229 +565,227 @@ pthread_t MtpStorage::inotify(void) { 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); +int MtpStorage::addInotify(Tree* tree) { + std::string path = getNodePath(tree); + MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str()); + int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS); + if (wd < 0) { + MTPE("inotify_add_watch failed: %s\n", strerror(errno)); return -1; } + inotifymap[wd] = tree; + return 0; +} - 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; +void MtpStorage::handleInotifyEvent(struct inotify_event* event) +{ + std::map::iterator it = inotifymap.find(event->wd); + if (it == inotifymap.end()) { + MTPE("Unable to locate inotify_wd: %i\n", event->wd); + return; + } + Tree* tree = it->second; + MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str()); + Node* node = tree->findEntryByName(basename(event->name)); + if (node && node->Mtpid() == handleCurrentlySending) { + MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid()); + return; + } + if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { + if (event->mask & IN_ISDIR) { + MTPD("inotify_t create is dir\n"); + } else { + MTPD("inotify_t create is file\n"); } - if (!(st.st_mode & S_IFDIR) || strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - if (addInotifyDirs(inotifypath)) { - closedir(d); - return -1; + if (node == NULL) { + node = addNewNode(event->mask & IN_ISDIR, tree, event->name); + std::string item = getNodePath(tree) + "/" + event->name; + node->addProperties(item, getStorageID()); + mServer->sendObjectAdded(node->Mtpid()); + } else { + MTPD("inotify_t item already exists.\n"); + } + if (event->mask & IN_ISDIR) { + // TODO: do we need to do anything here? probably not until someone reads from the dir... + } + } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { + if (event->mask & IN_ISDIR) { + MTPD("inotify_t Directory %s deleted\n", event->name); + } else { + MTPD("inotify_t File %s deleted\n", event->name); + } + if (node) + { + if (event->mask & IN_ISDIR) { + for (std::map::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) { + if (it->second == node) { + inotify_rm_watch(inotify_fd, it->first); + MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str()); + inotifymap.erase(it->first); + break; + } + + } + } + MtpObjectHandle handle = node->Mtpid(); + deleteFile(handle); + mServer->sendObjectRemoved(handle); + } else { + MTPD("inotify_t already removed.\n"); + } + } else if (event->mask & IN_MODIFY) { + MTPD("inotify_t item %s modified.\n", event->name); + if (node != NULL) { + uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt; + struct stat st; + uint64_t new_size = 0; + if (lstat(getNodePath(node).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: %u\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"); } - 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()); + } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) { + // TODO: is this always already handled by IN_DELETE for the parent dir? } - 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"); + MTPD("inotify thread: inotify_init\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); + if (inotify_fd < 0) { + MTPE("Can't run inotify for mtp server: %s\n", strerror(errno)); return -1; } while (true) { - i = 0; - len = read(inotify_fd, buf, EVENT_BUF_LEN); + int i = 0; + int len = read(inotify_fd, buf, EVENT_BUF_LEN); if (len < 0) { + if (errno == EINTR) + continue; 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"); - } - } - } + 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); + lockMutex(1); + handleInotifyEvent(event); + unlockMutex(1); } -end: - unlockMutex(1); i += EVENT_SIZE + event->len; } } - for (std::map::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) { + 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; +Node* MtpStorage::findNodeByPath(const std::string& path) { + MTPD("findNodeByPath: %s\n", path.c_str()); + std::string match = path.substr(0, mtpstorageparent.size()); + if (match != mtpstorageparent) { + // not on this device + MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str()); + return NULL; } - for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) { - node = i->second->findNodePath(parent_path, i->second->Root()); + + // TODO: fix and test this + std::string p = path.substr(mtpstorageparent.size()+1); // cut off "/" after storage root too + Tree* tree = mtpmap[0]; // start at storage root + + Node* node = NULL; + while (!p.empty()) { + size_t slashpos = p.find('/'); + std::string e; + if (slashpos != std::string::npos) { + e = p; + p.clear(); + } else { + e = p.substr(0, slashpos); + p = p.substr(slashpos + 1); + } + MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str()); + node = tree->findEntryByName(e); + if (!node) { + MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str()); + return NULL; + } + if (node->isDir()) + tree = static_cast(node); + else if (!p.empty()) { + MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node); + return NULL; + } + } + MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str()); + return node; +} + +Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name) +{ + // global counter for new object handles + static MtpObjectHandle mtpid = 0; + + ++mtpid; + MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid); + MtpObjectHandle parent = tree->Mtpid(); + MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str()); + Node* node; + if (isDir) + node = mtpmap[mtpid] = new Tree(mtpid, parent, name); + else + node = new Node(mtpid, parent, name); + tree->addEntry(node); + return node; +} + +Node* MtpStorage::findNode(MtpObjectHandle handle) { + for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) { + Node* node = i->second->findNode(handle); if (node != NULL) { - MTPD("MtpStorage::getParentObject for: '%s' returning: %i\n", parent_path.c_str(), node->Mtpid()); - return node->Mtpid(); + MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str()); + if (node->Mtpid() != handle) + { + MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid()); + } + return node; } } - MTPE("MtpStorage::getParentObject for: '%s' unable to locate node\n", parent_path.c_str()); - return -1; + // Item is not on this storage device + MTPE("MtpStorage::findNode: no node found for handle %u, searched %u trees\n", handle, mtpmap.size()); + return NULL; +} + +std::string MtpStorage::getNodePath(Node* node) { + std::string path; + MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid()); + while (node) + { + path = "/" + node->getName() + path; + MtpObjectHandle parent = node->getMtpParentId(); + if (parent == 0) // root + break; + node = findNode(parent); + } + path = mtpstorageparent + path; + MTPD("getNodePath: path %s\n", path.c_str()); + return path; } void MtpStorage::lockMutex(int thread_type) { diff --git a/mtp/MtpStorage.h b/mtp/MtpStorage.h index ad828d163..418e3db8c 100755 --- a/mtp/MtpStorage.h +++ b/mtp/MtpStorage.h @@ -30,6 +30,7 @@ #include "MtpServer.h" class MtpDatabase; +struct inotify_event; class MtpStorage { @@ -43,16 +44,10 @@ private: uint64_t mReserveSpace; bool mRemovable; MtpServer* mServer; - std::deque mtpParentList; - int mtpparentid; - Tree *mtpdbtree; typedef std::map maptree; typedef maptree::iterator iter; maptree mtpmap; std::string mtpstorageparent; - pthread_t inotify_thread; - int inotify_fd; - int inotify_wd; android::Mutex mMutex; public: @@ -71,30 +66,50 @@ public: inline const char* getPath() const { return (const char *)mFilePath; } inline bool isRemovable() const { return mRemovable; } inline uint64_t getMaxFileSize() const { return mMaxFileSize; } - int readParentDirs(std::string path); + + struct PropEntry { + MtpObjectHandle handle; + uint16_t property; + uint16_t datatype; + uint64_t intvalue; + std::string strvalue; + }; + + int readDir(const std::string& path, Tree* tree); int createDB(); MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectHandle parent); int getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info); - MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified); + MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, uint64_t size, time_t modified); + void endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded); int getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet); int getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat); int deleteFile(MtpObjectHandle handle); int renameObject(MtpObjectHandle handle, std::string newName); - int getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, uint64_t &longValue); + int getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, PropEntry& prop); void lockMutex(int thread_type); void unlockMutex(int thread_type); private: - void createEmptyDir(const char* path); pthread_t inotify(); int inotify_t(); typedef int (MtpStorage::*ThreadPtr)(void); typedef void* (*PThreadPtr)(void *); - std::map inotifymap; - int addInotifyDirs(std::string path); - void deleteTrees(int parent); + std::map inotifymap; // inotify wd -> tree + pthread_t inotify_thread; + int inotify_fd; + int addInotify(Tree* tree); + void handleInotifyEvent(struct inotify_event* event); + bool sendEvents; - int getParentObject(std::string parent_path); + MtpObjectHandle handleCurrentlySending; + + Node* addNewNode(bool isDir, Tree* tree, const std::string& name); + Node* findNode(MtpObjectHandle handle); + Node* findNodeByPath(const std::string& path); + std::string getNodePath(Node* node); + + void queryNodeProperties(std::vector& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID); + bool use_mutex; pthread_mutex_t inMutex; // inotify mutex pthread_mutex_t mtpMutex; // main mtp mutex diff --git a/mtp/btree.cpp b/mtp/btree.cpp index 7976ac325..3a5648db0 100755 --- a/mtp/btree.cpp +++ b/mtp/btree.cpp @@ -20,289 +20,60 @@ #include "MtpDebug.h" // Constructor -Tree::Tree() { - root = NULL; - count = 0; +Tree::Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name) + : Node(handle, parent, name), alreadyRead(false) { } // Destructor Tree::~Tree() { - freeNode(root); -} - -// Free the node -void Tree::freeNode(Node* leaf) -{ - if ( leaf != NULL ) - { - freeNode(leaf->Left()); - freeNode(leaf->Right()); - delete leaf; - } + for (std::map::iterator it = entries.begin(); it != entries.end(); ++it) + delete it->second; } int Tree::getCount(void) { + int count = entries.size(); MTPD("node count: %d\n", count); return count; } -Node* Tree::addNode(int mtpid, std::string path) -{ - MTPD("root: %d\n", root); - // No elements. Add the root - if ( root == NULL ) { - Node* n = new Node(); - count++; - MTPD("node count: %d\n", count); - MTPD("adding node address: %d\n", n); - MTPD("adding mtpid: %d\n", mtpid); - n->setMtpid(mtpid); - n->setPath(path); - root = n; - MTPD("set root to %d\n", root); - return n; - } - else { - count++; - MTPD("node count: %d\n", count); - MTPD("adding new child node\n"); - return addNode(mtpid, root, path); - } -} - -// Add a node (private) -Node* Tree::addNode(int mtpid, Node* leaf, std::string path) { - Node* n; - if ( mtpid <= leaf->Mtpid() ) - { - if ( leaf->Left() != NULL ) - return addNode(mtpid, leaf->Left(), path); - else { - n = new Node(); - MTPD("adding mtpid: %d node: %d\n", mtpid, n); - n->setMtpid(mtpid); - n->setPath(path); - n->setParent(leaf); - leaf->setLeft(n); - } - } - else - { - if ( leaf->Right() != NULL ) - return addNode(mtpid, leaf->Right(), path); - else { - n = new Node(); - MTPD("adding mtpid: %d node: %d\n", mtpid, n); - n->setMtpid(mtpid); - n->setPath(path); - n->setParent(leaf); - leaf->setRight(n); - } - } - return n; -} - -void Tree::setMtpParentId(int mtpparentid, Node* node) { - node->setMtpParentId(mtpparentid); -} - -std::string Tree::getPath(Node* node) { - return node->getPath(); -} - -int Tree::getMtpParentId(Node* node) { - return node->getMtpParentId(); -} - -Node* Tree::findNodePath(std::string path, Node* node) { - Node* n; - if ( node == NULL ) { - return NULL; - } - if ( node->getPath().compare(path) == 0 && node->Mtpid() > 0) { - return node; - } - else { - n = findNodePath(path, node->Left()); - if (n) - return n; - n = findNodePath(path, node->Right()); - if (n) - return n; - } - return NULL; -} - -Node* Tree::getNext(Node *node) { - if (node == NULL) - return NULL; - else { - if (node->Left() != NULL) - return node->Left(); - if (node->Right() != NULL) - return node->Right(); - } - return NULL; -} - -Node* Tree::findNode(int key, Node* node) { - //MTPD("key: %d\n", key); - //MTPD("node: %d\n", node); - if ( node == NULL ) { - return NULL; - } - else if ( node->Mtpid() == key ) { - return node; - } - else if ( key <= node->Mtpid() ) { - return findNode(key, node->Left()); - } - else if ( key > node->Mtpid() ) { - return findNode(key, node->Right()); +void Tree::addEntry(Node* node) { + if (node->Mtpid() == 0) { + MTPE("Tree::addEntry: not adding node with 0 handle.\n"); + return; } - else { - return NULL; + if (node->Mtpid() == node->getMtpParentId()) { + MTPE("Tree::addEntry: not adding node with handle %u == parent.\n", node->Mtpid()); + return; } - return NULL; + entries[node->Mtpid()] = node; } -void Tree::getmtpids(Node* node, std::vector* mtpids) -{ - if ( node ) +Node* Tree::findEntryByName(std::string name) { + for (std::map::iterator it = entries.begin(); it != entries.end(); ++it) { - MTPD("node: %d\n", node->Mtpid()); - mtpids->push_back(node->Mtpid()); - if (node->Left()) - getmtpids(node->Left(), mtpids); - if (node->Right()) - getmtpids(node->Right(), mtpids); - } else { - mtpids->push_back(0); + Node* node = it->second; + if (node->getName().compare(name) == 0 && node->Mtpid() > 0) + return node; } - return; -} - -// Find the node with min key -// Traverse the left sub-tree recursively -// till left sub-tree is empty to get min -Node* Tree::min(Node* node) -{ - if ( node == NULL ) - return NULL; - - if ( node->Left() ) - min(node->Left()); - else - return node; - return NULL; -} - -// Find the node with max key -// Traverse the right sub-tree recursively -// till right sub-tree is empty to get max -Node* Tree::max(Node* node) -{ - if ( node == NULL ) - return NULL; - - if ( node->Right() ) - max(node->Right()); - else - return node; return NULL; } -// Find successor to a node -// Find the node, get the node with max value -// for the right sub-tree to get the successor -Node* Tree::successor(int key, Node *node) -{ - Node* thisKey = findNode(key, node); - if ( thisKey ) - return max(thisKey->Right()); +Node* Tree::findNode(MtpObjectHandle handle) { + std::map::iterator it = entries.find(handle); + if (it != entries.end()) + return it->second; return NULL; } -// Find predecessor to a node -// Find the node, get the node with max value -// for the left sub-tree to get the predecessor -Node* Tree::predecessor(int key, Node *node) -{ - Node* thisKey = findNode(key, node); - if ( thisKey ) - return max(thisKey->Left()); - return NULL; +void Tree::getmtpids(MtpObjectHandleList* mtpids) { + for (std::map::iterator it = entries.begin(); it != entries.end(); ++it) + mtpids->push_back(it->second->Mtpid()); } -void Tree::deleteNode(int key) -{ - // Find the node. - Node* thisKey = findNode(key, root); - MTPD("Tree::deleteNode found node: %d\n", thisKey); - MTPD("handle: %d\n", thisKey->Mtpid()); - - if (thisKey == root) { - if (thisKey->Right()) { - root = thisKey->Right(); - root->setParent(NULL); - return; - } - if (thisKey->Left()) { - root = thisKey->Left(); - root->setParent(NULL); - return; - } - root = NULL; - delete thisKey; - return; - } - - if ( thisKey->Left() == NULL && thisKey->Right() == NULL ) - { - if ( thisKey->Mtpid() > thisKey->Parent()->Mtpid() ) { - thisKey->Parent()->setRight(NULL); - } - else { - thisKey->Parent()->setLeft(NULL); - } - delete thisKey; - return; - } - - if ( thisKey->Left() == NULL && thisKey->Right() != NULL ) - { - if ( thisKey->Mtpid() > thisKey->Parent()->Mtpid() ) - thisKey->Parent()->setRight(thisKey->Right()); - else - thisKey->Parent()->setLeft(thisKey->Right()); - thisKey->Right()->setParent(thisKey->Parent()); - delete thisKey; - return; - } - if ( thisKey->Left() != NULL && thisKey->Right() == NULL ) - { - if ( thisKey->Mtpid() > thisKey->Parent()->Mtpid() ) - thisKey->Parent()->setRight(thisKey->Left()); - else - thisKey->Parent()->setLeft(thisKey->Left()); - thisKey->Left()->setParent(thisKey->Parent()); - delete thisKey; - return; - } - - if ( thisKey->Left() != NULL && thisKey->Right() != NULL ) - { - Node* sub = predecessor(thisKey->Mtpid(), thisKey); - if ( sub == NULL ) - sub = successor(thisKey->Mtpid(), thisKey); - - if ( sub->Parent()->Mtpid() <= sub->Mtpid() ) - sub->Parent()->setRight(sub->Right()); - else - sub->Parent()->setLeft(sub->Left()); - - thisKey->setMtpid(sub->Mtpid()); - delete sub; - return; +void Tree::deleteNode(MtpObjectHandle handle) { + std::map::iterator it = entries.find(handle); + if (it != entries.end()) { + delete it->second; + entries.erase(it); } } diff --git a/mtp/btree.hpp b/mtp/btree.hpp index 1fa8d2800..b284e4f4b 100755 --- a/mtp/btree.hpp +++ b/mtp/btree.hpp @@ -17,79 +17,65 @@ #ifndef BTREE_HPP #define BTREE_HPP -#include #include -#include -#include "MtpDebug.h" +#include +#include "MtpTypes.h" -// A generic tree node class +// A directory entry class Node { - int mtpid; - int mtpparentid; - std::string path; - int parentID; - Node* left; - Node* right; - Node* parent; + MtpObjectHandle handle; + MtpObjectHandle parent; + std::string name; // name only without path public: Node(); - void setMtpid(int aMtpid); - void setPath(std::string aPath); - void rename(std::string aPath); - void setLeft(Node* aLeft); - void setRight(Node* aRight); - void setParent(Node* aParent); - void setMtpParentId(int id); - int Mtpid(); - int getMtpParentId(); - std::string getPath(); - Node* Left(); - Node* Right(); - Node* Parent(); - void addProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType); - void updateProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType); - void addProperties(int storageID, int parent_object); - uint64_t getIntProperty(uint64_t property); + Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name); + virtual ~Node() {} + + virtual bool isDir() const { return false; } + + void rename(const std::string& newName); + MtpObjectHandle Mtpid() const; + MtpObjectHandle getMtpParentId() const; + const std::string& getName() const; + + void addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType); + void updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType); + void addProperties(const std::string& path, int storageID); + uint64_t getIntProperty(MtpPropertyCode property); struct mtpProperty { - uint64_t property; + MtpPropertyCode property; + MtpDataType dataType; uint64_t valueInt; std::string valueStr; - int dataType; + mtpProperty() : property(0), dataType(0), valueInt(0) {} }; std::vector& getMtpProps(); std::vector mtpProp; + const mtpProperty& getProperty(MtpPropertyCode property); }; -// Binary Search Tree class -class Tree { - Node* root; +// A directory +class Tree : public Node { + std::map entries; + bool alreadyRead; public: - Tree(); + Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name); ~Tree(); - Node* Root() { - MTPD("root: %d\n", root); - return root; - }; - Node* addNode(int mtpid, std::string path); - void setMtpParentId(int mtpparentid, Node* node); - Node* findNode(int key, Node* parent); - void getmtpids(Node* node, std::vector* mtpids); - void deleteNode(int key); - Node* min(Node* node); - Node* max(Node* node); - Node* successor(int key, Node* parent); - Node* predecessor(int key, Node* parent); + + virtual bool isDir() const { return true; } + + void addEntry(Node* node); + Node* findNode(MtpObjectHandle handle); + void getmtpids(MtpObjectHandleList* mtpids); + void deleteNode(MtpObjectHandle handle); std::string getPath(Node* node); + int getMtpParentId() { return Node::getMtpParentId(); } int getMtpParentId(Node* node); - Node* findNodePath(std::string path, Node* node); - Node* getNext(Node* node); + Node* findEntryByName(std::string name); int getCount(); - -private: - Node* addNode(int mtpid, Node* leaf, std::string path); - void freeNode(Node* leaf); - int count; + bool wasAlreadyRead() const { return alreadyRead; } + void setAlreadyRead(bool b) { alreadyRead = b; } }; #endif diff --git a/mtp/mtp_MtpDatabase.cpp b/mtp/mtp_MtpDatabase.cpp index b5096fe76..05bb5d9fb 100755 --- a/mtp/mtp_MtpDatabase.cpp +++ b/mtp/mtp_MtpDatabase.cpp @@ -42,7 +42,7 @@ #include "MtpUtils.h" #include "mtp.h" #include "mtp_MtpDatabase.hpp" -#include "btree.hpp" +//#include "btree.hpp" MyMtpDatabase::MyMtpDatabase() { @@ -75,6 +75,7 @@ int MyMtpDatabase::FILE_PROPERTIES[10] = { MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, MTP_PROPERTY_NAME, + // TODO: why is DISPLAY_NAME not here? MTP_PROPERTY_DATE_ADDED }; @@ -214,8 +215,9 @@ MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path, MtpStorageID storage, uint64_t size, time_t modified) { - MTPD("MyMtpDatabase::beginSendObject() which passes to MtpStorage.cpp\n"); - return storagemap[storage]->beginSendObject(path, format, parent, storage, size, modified); + if (storagemap.find(storage) == storagemap.end()) + return kInvalidObjectHandle; + return storagemap[storage]->beginSendObject(path, format, parent, size, modified); } void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, @@ -225,6 +227,9 @@ void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, MTPE("endSendObject() failed, unlinking %s\n", path); unlink(path); } + std::map::iterator storit; + for (storit = storagemap.begin(); storit != storagemap.end(); storit++) + storit->second->endSendObject(path, handle, format, succeeded); } void MyMtpDatabase::createDB(MtpStorage* storage, MtpStorageID storageID) { @@ -236,8 +241,7 @@ MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { MTPD("storageID: %d\n", storageID); - MtpObjectHandleList* list = new MtpObjectHandleList(); - list = storagemap[storageID]->getObjectList(storageID, parent); + MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent); MTPD("list: %d\n", list->size()); return list; } @@ -245,22 +249,20 @@ MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, int MyMtpDatabase::getNumObjects(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { - MTPE("MyMtpDatabase::getNumObjects not implemented, returning 0\n"); - int result = 0; - //get number of objects on filesystem storage - return result; + MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent); + int size = list->size(); + delete list; + return size; } MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() { // This function tells the host PC which file formats the device supports - int* formats; MtpObjectFormatList* list = new MtpObjectFormatList(); - formats = SUPPORTED_PLAYBACK_FORMATS; - int length = sizeof(formats)/ sizeof(int); + int length = sizeof(SUPPORTED_PLAYBACK_FORMATS) / sizeof(SUPPORTED_PLAYBACK_FORMATS[0]); MTPD("MyMtpDatabase::getSupportedPlaybackFormats length: %i\n", length); for (int i = 0; i < length; i++) { - MTPD("supported playback format: %d\n", formats[i]); - list->push(formats[i]); + MTPD("supported playback format: %x\n", SUPPORTED_PLAYBACK_FORMATS[i]); + list->push(SUPPORTED_PLAYBACK_FORMATS[i]); } return list; } @@ -284,25 +286,29 @@ MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectForm case MTP_FORMAT_AAC: properties = AUDIO_PROPERTIES; length = sizeof(AUDIO_PROPERTIES) / sizeof(AUDIO_PROPERTIES[0]); + break; case MTP_FORMAT_MPEG: case MTP_FORMAT_3GP_CONTAINER: case MTP_FORMAT_WMV: properties = VIDEO_PROPERTIES; length = sizeof(VIDEO_PROPERTIES) / sizeof(VIDEO_PROPERTIES[0]); + break; case MTP_FORMAT_EXIF_JPEG: case MTP_FORMAT_GIF: case MTP_FORMAT_PNG: case MTP_FORMAT_BMP: properties = IMAGE_PROPERTIES; length = sizeof(IMAGE_PROPERTIES) / sizeof(IMAGE_PROPERTIES[0]); + break; case 0: properties = ALL_PROPERTIES; length = sizeof(ALL_PROPERTIES) / sizeof(ALL_PROPERTIES[0]); + break; default: properties = FILE_PROPERTIES; length = sizeof(FILE_PROPERTIES) / sizeof(FILE_PROPERTIES[0]); } - MTPD("MyMtpDatabase::getSupportedObjectProperties length is: %i, format: %x, sizeof: %i", length, format); + MTPD("MyMtpDatabase::getSupportedObjectProperties length is: %i, format: %x", length, format); for (int i = 0; i < length; i++) { MTPD("supported object property: %x\n", properties[i]); list->push(properties[i]); @@ -313,35 +319,37 @@ MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectForm MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() { MtpDevicePropertyList* list = new MtpDevicePropertyList(); int length = sizeof(DEVICE_PROPERTIES) / sizeof(DEVICE_PROPERTIES[0]); - MTPD("MyMtpDatabase::getSupportedDeviceProperties length was: %i\n"); + MTPD("MyMtpDatabase::getSupportedDeviceProperties length was: %i\n", length); for (int i = 0; i < length; i++) list->push(DEVICE_PROPERTIES[i]); return list; } + MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet) { - MTPD("MyMtpDatabase::getObjectPropertyValue mtpid: %i, property: %x\n", handle, property); + MTPD("MyMtpDatabase::getObjectPropertyValue mtpid: %u, property: %x\n", handle, property); int type; MtpResponseCode result = MTP_RESPONSE_INVALID_OBJECT_HANDLE; - uint64_t longValue; + MtpStorage::PropEntry prop; if (!getObjectPropertyInfo(property, type)) { - MTPE("MyMtpDatabase::setObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n"); + MTPE("MyMtpDatabase::getObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n"); return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; } std::map::iterator storit; for (storit = storagemap.begin(); storit != storagemap.end(); storit++) { - if (storit->second->getObjectPropertyValue(handle, property, longValue) == 0) { + if (storit->second->getObjectPropertyValue(handle, property, prop) == 0) { result = MTP_RESPONSE_OK; break; } } if (result != MTP_RESPONSE_OK) { - MTPE("MyMtpDatabase::setObjectPropertyValue unable to locate handle: %i\n", handle); + MTPE("MyMtpDatabase::getObjectPropertyValue unable to locate handle: %u\n", handle); return MTP_RESPONSE_INVALID_OBJECT_HANDLE; } + uint64_t longValue = prop.intvalue; // special case date properties, which are strings to MTP // but stored internally as a uint64 if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) { @@ -387,7 +395,7 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, packet.putInt128(longValue); break; case MTP_TYPE_UINT128: - packet.putInt128(longValue); + packet.putUInt128(longValue); break; case MTP_TYPE_STR: { @@ -401,8 +409,10 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, } else { packet.putEmptyString(); }*/ - MTPE("STRING unsupported type in getObjectPropertyValue\n"); - result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; + packet.putString(prop.strvalue.c_str()); + MTPD("MTP_TYPE_STR: %x = %s\n", prop.property, prop.strvalue.c_str()); + //MTPE("STRING unsupported type in getObjectPropertyValue\n"); + //result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; break; } default: @@ -764,26 +774,21 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, switch (property) { case MTP_PROPERTY_OBJECT_FORMAT: // use format as default value - MTPD("MyMtpDatabase::getObjectPropertyDesc format\n"); result = new MtpProperty(property, MTP_TYPE_UINT16, false, format); break; case MTP_PROPERTY_PROTECTION_STATUS: case MTP_PROPERTY_TRACK: - MTPD("MyMtpDatabase::getObjectPropertyDesc track\n"); result = new MtpProperty(property, MTP_TYPE_UINT16); break; case MTP_PROPERTY_STORAGE_ID: case MTP_PROPERTY_PARENT_OBJECT: case MTP_PROPERTY_DURATION: - MTPD("MyMtpDatabase::getObjectPropertyDesc duration\n"); result = new MtpProperty(property, MTP_TYPE_UINT32); break; case MTP_PROPERTY_OBJECT_SIZE: - MTPD("MyMtpDatabase::getObjectPropertyDesc size\n"); result = new MtpProperty(property, MTP_TYPE_UINT64); break; case MTP_PROPERTY_PERSISTENT_UID: - MTPD("MyMtpDatabase::getObjectPropertyDesc persistent uid\n"); result = new MtpProperty(property, MTP_TYPE_UINT128); break; case MTP_PROPERTY_NAME: @@ -794,18 +799,15 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, case MTP_PROPERTY_GENRE: case MTP_PROPERTY_COMPOSER: case MTP_PROPERTY_DESCRIPTION: - MTPD("MyMtpDatabase::getObjectPropertyDesc description\n"); result = new MtpProperty(property, MTP_TYPE_STR); break; case MTP_PROPERTY_DATE_MODIFIED: case MTP_PROPERTY_DATE_ADDED: case MTP_PROPERTY_ORIGINAL_RELEASE_DATE: - MTPD("MyMtpDatabase::getObjectPropertyDesc date\n"); result = new MtpProperty(property, MTP_TYPE_STR); result->setFormDateTime(); break; case MTP_PROPERTY_OBJECT_FILE_NAME: - MTPD("MyMtpDatabase::getObjectPropertyDesc file name\n"); // We allow renaming files and folders result = new MtpProperty(property, MTP_TYPE_STR, true); break; @@ -816,26 +818,19 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { MtpProperty* result = NULL; int ret; - bool writable = true; + bool writable = false; switch (property) { case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER: case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: - ret = MTP_RESPONSE_OK; + writable = true; // fall through case MTP_DEVICE_PROPERTY_IMAGE_SIZE: result = new MtpProperty(property, MTP_TYPE_STR, writable); - ret = MTP_RESPONSE_OK; // get current value - if (ret == MTP_RESPONSE_OK) { - result->setCurrentValue('\0'); - result->setDefaultValue('\0'); - } else { - MTPE("unable to read device property, response: %04X", ret); - } - break; - default: - ret = MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; + // TODO: add actual values + result->setCurrentValue('\0'); + result->setDefaultValue('\0'); break; } @@ -854,26 +849,6 @@ void MyMtpDatabase::sessionEnded() { // ---------------------------------------------------------------------------- -static void -android_mtp_MtpDatabase_setup() -{ - MyMtpDatabase* database = new MyMtpDatabase(); -} - -static void -android_mtp_MtpDatabase_finalize() -{ - return; -} - -static std::string -android_mtp_MtpPropertyGroup_format_date_time(long seconds) -{ - char date[20]; - formatDateTime(seconds, date, sizeof(date)); - return date; -} - void MyMtpDatabase::lockMutex(void) { std::map::iterator storit; for (storit = storagemap.begin(); storit != storagemap.end(); storit++) { diff --git a/mtp/node.cpp b/mtp/node.cpp index 17047ce87..1bca1d96f 100755 --- a/mtp/node.cpp +++ b/mtp/node.cpp @@ -33,38 +33,28 @@ #include "MtpDebug.h" -Node::Node() { - mtpid= -1; - path = ""; - left = NULL; - right = NULL; - parent = NULL; - mtpparentid = 0; +Node::Node() + : handle(-1), parent(0), name("") +{ } -void Node::setMtpid(int aMtpid) { mtpid = aMtpid; } -void Node::setPath(std::string aPath) { path = aPath; } -void Node::rename(std::string aPath) { - path = aPath; - updateProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, basename(aPath.c_str()), MTP_TYPE_STR); - updateProperty(MTP_PROPERTY_NAME, 0, basename(aPath.c_str()), MTP_TYPE_STR); - updateProperty(MTP_PROPERTY_DISPLAY_NAME, 0, basename(aPath.c_str()), MTP_TYPE_STR); +Node::Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name) + : handle(handle), parent(parent), name(name) +{ } -void Node::setLeft(Node* aLeft) { left = aLeft; } -void Node::setRight(Node* aRight) { right = aRight; } -void Node::setParent(Node* aParent) { parent = aParent; } -void Node::setMtpParentId(int id) { - mtpparentid = id; - MTPD("setting mtpparentid: %i on mtpid: %i\n", mtpparentid, mtpid); + +void Node::rename(const std::string& newName) { + name = newName; + updateProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, name.c_str(), MTP_TYPE_STR); + updateProperty(MTP_PROPERTY_NAME, 0, name.c_str(), MTP_TYPE_STR); + updateProperty(MTP_PROPERTY_DISPLAY_NAME, 0, name.c_str(), MTP_TYPE_STR); } -int Node::Mtpid() { return mtpid; } -int Node::getMtpParentId() { return mtpparentid; } -std::string Node::getPath() { return path; } -Node* Node::Left() { return left; } -Node* Node::Right() { return right; } -Node* Node::Parent() { return parent; } -uint64_t Node::getIntProperty(uint64_t property) { +MtpObjectHandle Node::Mtpid() const { return handle; } +MtpObjectHandle Node::getMtpParentId() const { return parent; } +const std::string& Node::getName() const { return name; } + +uint64_t Node::getIntProperty(MtpPropertyCode property) { for (unsigned index = 0; index < mtpProp.size(); ++index) { if (mtpProp[index].property == property) return mtpProp[index].valueInt; @@ -73,8 +63,18 @@ uint64_t Node::getIntProperty(uint64_t property) { return -1; } -void Node::addProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType) { - MTPD("adding str property: %lld, valueInt: %lld, valueStr: %s, dataType: %d\n", property, valueInt, valueStr.c_str(), dataType); +const Node::mtpProperty& Node::getProperty(MtpPropertyCode property) { + static const mtpProperty dummyProp; + for (size_t i = 0; i < mtpProp.size(); ++i) { + if (mtpProp[i].property == property) + return mtpProp[i]; + } + MTPE("Node::getProperty failed to find property %x, returning dummy property\n", (unsigned)property); + return dummyProp; +} + +void Node::addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) { +// MTPD("adding property: %lld, valueInt: %lld, valueStr: %s, dataType: %d\n", property, valueInt, valueStr.c_str(), dataType); struct mtpProperty prop; prop.property = property; prop.valueInt = valueInt; @@ -83,7 +83,7 @@ void Node::addProperty(uint64_t property, uint64_t valueInt, std::string valueSt mtpProp.push_back(prop); } -void Node::updateProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType) { +void Node::updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) { for (unsigned i = 0; i < mtpProp.size(); i++) { if (mtpProp[i].property == property) { mtpProp[i].valueInt = valueInt; @@ -99,43 +99,45 @@ std::vector& Node::getMtpProps() { return mtpProp; } -void Node::addProperties(int storageID, int parent_object) { +void Node::addProperties(const std::string& path, int storageID) { + MTPD("addProperties: handle: %u, filename: '%s'\n", handle, getName().c_str()); struct stat st; int mFormat = 0; uint64_t puid; off_t file_size = 0; - std::string mtimeStr = "00101T000000"; - std::string mtpidStr = static_cast( &(std::ostringstream() << mtpid) )->str(); + std::string mtpidStr = static_cast( &(std::ostringstream() << handle) )->str(); std::string storageIDStr = static_cast( &(std::ostringstream() << storageID) )->str(); std::string puidStr = storageIDStr + mtpidStr; if ( ! (std::istringstream(puidStr) >> puid) ) puid = 0; mFormat = MTP_FORMAT_UNDEFINED; // file - if (lstat(getPath().c_str(), &st) == 0) { + if (lstat(path.c_str(), &st) == 0) { file_size = st.st_size; if (S_ISDIR(st.st_mode)) mFormat = MTP_FORMAT_ASSOCIATION; // folder - mtimeStr = static_cast( &(std::ostringstream() << st.st_mtime) )->str(); } + // TODO: don't store properties with constant values at all, add them at query time instead addProperty(MTP_PROPERTY_STORAGE_ID, storageID, "", MTP_TYPE_UINT32); addProperty(MTP_PROPERTY_OBJECT_FORMAT, mFormat, "", MTP_TYPE_UINT16); addProperty(MTP_PROPERTY_PROTECTION_STATUS, 0, "", MTP_TYPE_UINT16); addProperty(MTP_PROPERTY_OBJECT_SIZE, file_size, "", MTP_TYPE_UINT64); - addProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, basename(getPath().c_str()), MTP_TYPE_STR); - MTPD("mtpid: %i, filename: '%s', parent object: %i\n", mtpid, basename(getPath().c_str()), parent_object); - addProperty(MTP_PROPERTY_DATE_MODIFIED, 0, mtimeStr, MTP_TYPE_STR); - addProperty(MTP_PROPERTY_PARENT_OBJECT, parent_object, "", MTP_TYPE_UINT32); + addProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, getName().c_str(), MTP_TYPE_STR); + addProperty(MTP_PROPERTY_DATE_MODIFIED, st.st_mtime, "", MTP_TYPE_UINT64); + addProperty(MTP_PROPERTY_PARENT_OBJECT, parent, "", MTP_TYPE_UINT32); addProperty(MTP_PROPERTY_PERSISTENT_UID, puid, "", MTP_TYPE_UINT128); - addProperty(MTP_PROPERTY_NAME, 0, basename(getPath().c_str()), MTP_TYPE_STR); - addProperty(MTP_PROPERTY_DISPLAY_NAME, 0, basename(getPath().c_str()), MTP_TYPE_STR); - addProperty(MTP_PROPERTY_DATE_ADDED, 0, mtimeStr, MTP_TYPE_STR); + // TODO: we can't really support persistent UIDs without a persistent DB. + // probably a combination of volume UUID + st_ino would come close. + // doesn't help for fs with no native inodes numbers like fat though... + addProperty(MTP_PROPERTY_NAME, 0, getName().c_str(), MTP_TYPE_STR); + addProperty(MTP_PROPERTY_DISPLAY_NAME, 0, getName().c_str(), MTP_TYPE_STR); + addProperty(MTP_PROPERTY_DATE_ADDED, st.st_mtime, "", MTP_TYPE_UINT64); addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR); addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR); addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR); addProperty(MTP_PROPERTY_ALBUM_ARTIST, 0, "", MTP_TYPE_STR); addProperty(MTP_PROPERTY_TRACK, 0, "", MTP_TYPE_UINT16); - addProperty(MTP_PROPERTY_ORIGINAL_RELEASE_DATE, 0, "00101T000000", MTP_TYPE_STR); + addProperty(MTP_PROPERTY_ORIGINAL_RELEASE_DATE, 2014, "", MTP_TYPE_UINT64); // TODO: extract year from st.st_mtime? addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32); addProperty(MTP_PROPERTY_GENRE, 0, "", MTP_TYPE_STR); addProperty(MTP_PROPERTY_COMPOSER, 0, "", MTP_TYPE_STR); -- cgit v1.2.3