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
diff --git a/mtp/MtpStorage.cpp b/mtp/MtpStorage.cpp
index 57c8774..4c55361 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 <sys/types.h>
 #include <sys/stat.h>
@@ -35,12 +36,9 @@
 #include <signal.h>
 #include <sys/inotify.h>
 #include <fcntl.h>
-#include <sstream>
 
 #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 @@
 		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() {
 	if (inotify_thread) {
+		// TODO: what does this do? manpage says it does not kill the thread
 		pthread_kill(inotify_thread, 0);
-		for (std::map<int, std::string>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+		for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
 			inotify_rm_watch(inotify_fd, i->first);
 		}
 		close(inotify_fd);
@@ -124,12 +123,8 @@
 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 @@
 	} 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<int> 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;
+		parent = 0;
 	}
-	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");
+	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;
-	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;
-			}
-		}
+	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;
 	}
-	MTPE("MtpStorage::beginSendObject(), path: '%s', parent: %d, storage: %d, format: %04x\n", path, parent, storage, format);
-	return -1;
+	Tree* tree = it->second;
+
+	std::string pathstr(path);
+	size_t slashpos = pathstr.find_last_of('/');
+	if (slashpos == std::string::npos) {
+		MTPE("path has no slash, returning error\n");
+		return kInvalidObjectHandle;
+	}
+	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;
-				}
-			}
-		}
+		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
 	}
-	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);
-	}
+	closedir(d);
+	// TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
+	tree->setAlreadyRead(true);
+	addInotify(tree);
 	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<int> propertyCodes;
-	std::vector<int> dataTypes;
-	std::vector<std::string> valueStrs;
-	std::vector<int> 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<Node::mtpProperty> 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
+int MtpStorage::deleteFile(MtpObjectHandle handle) {
+	MTPD("MtpStorage::deleteFile handle: %u\n", handle);
+	Node* node = findNode(handle);
+	if (!node) {
+		// Item is not on this storage device
 		return -1;
 	}
-	// handle not found on this storage
-	return -1;
+	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);
+	}
 
-endloop:
-	MTPD("mtpparentid: %d\n", local_mtpparentid);
-	MTPD("count: %d\n", count);
-	packet.putUInt32(count);
+	MTPD("deleting handle: %u\n", handle);
+	tree->deleteNode(handle);
+	MTPD("deleted\n");
+	return 0;
+}
 
-	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;
+void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& 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<Node::mtpProperty> 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);
+		}
+		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;
+		}
+
+		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
+		}
+
+	}
+	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<PropEntry> 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 {
+		// single object
+		Node* node = findNode(handle);
+		if (!node) {
+			// Item is not on this storage device
+			return -1;
+		}
+		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 @@
 	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 @@
 	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;
 	}
-
-	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);
+	inotifymap[wd] = tree;
 	return 0;
 }
 
+void MtpStorage::handleInotifyEvent(struct inotify_event* event)
+{
+	std::map<int, Tree*>::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 (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<int, Tree*>::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");
+		}
+	} 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?
+	}
+}
+
 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<int, std::string>::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<int, std::string>::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<int, std::string>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+	for (std::map<int, Tree*>::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());
-		if (node != NULL) {
-			MTPD("MtpStorage::getParentObject for: '%s' returning: %i\n", parent_path.c_str(), node->Mtpid());
-			return node->Mtpid();
+
+	// 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<Tree*>(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;
 		}
 	}
-	MTPE("MtpStorage::getParentObject for: '%s' unable to locate node\n", parent_path.c_str());
-	return -1;
+	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("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;
+		}
+	}
+	// 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 ad828d1..418e3db 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 @@
     uint64_t                mReserveSpace;
     bool                    mRemovable;
 	MtpServer*				mServer;
-	std::deque<std::string> mtpParentList;
-	int mtpparentid;
-    Tree *mtpdbtree;
     typedef std::map<int, Tree*> 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 @@
     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<int, std::string> inotifymap;
-	int addInotifyDirs(std::string path);
-	void deleteTrees(int parent);
+	std::map<int, Tree*> 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<PropEntry>& 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 7976ac3..3a5648d 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<MtpObjectHandle, Node*>::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());
-	}
-	else {
-		return NULL;
-	}
-	return NULL;
-}
-
-void Tree::getmtpids(Node* node, std::vector<int>* mtpids)
-{
-	if ( node )
-	{
-		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);
-	}
-	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());
-	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::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;
+void Tree::addEntry(Node* node) {
+	if (node->Mtpid() == 0) {
+		MTPE("Tree::addEntry: not adding node with 0 handle.\n");
 		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;
+	if (node->Mtpid() == node->getMtpParentId()) {
+		MTPE("Tree::addEntry: not adding node with handle %u == parent.\n", node->Mtpid());
 		return;
 	}
+	entries[node->Mtpid()] = node;
+}
 
-	if ( thisKey->Left() == NULL && thisKey->Right() != NULL )
+Node* Tree::findEntryByName(std::string name) {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
 	{
-		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;
+		Node* node = it->second;
+		if (node->getName().compare(name) == 0 && node->Mtpid() > 0)
+			return node;
 	}
-	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;
-	}
+	return NULL;
+}
 
-	if ( thisKey->Left() != NULL && thisKey->Right() != NULL )
-	{
-		Node* sub = predecessor(thisKey->Mtpid(), thisKey);
-		if ( sub == NULL )
-			sub = successor(thisKey->Mtpid(), thisKey);
+Node* Tree::findNode(MtpObjectHandle handle) {
+	std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);	
+	if (it != entries.end())
+		return it->second;
+	return NULL;
+}
 
-		if ( sub->Parent()->Mtpid() <= sub->Mtpid() )
-			sub->Parent()->setRight(sub->Right());
-		else
-			sub->Parent()->setLeft(sub->Left());
+void Tree::getmtpids(MtpObjectHandleList* mtpids) {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+		mtpids->push_back(it->second->Mtpid());
+}
 
-		thisKey->setMtpid(sub->Mtpid());
-		delete sub;
-		return;
+void Tree::deleteNode(MtpObjectHandle handle) {
+	std::map<MtpObjectHandle, Node*>::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 1fa8d28..b284e4f 100755
--- a/mtp/btree.hpp
+++ b/mtp/btree.hpp
@@ -17,79 +17,65 @@
 #ifndef BTREE_HPP
 #define BTREE_HPP
 
-#include <iostream>
 #include <vector>
-#include <utils/threads.h>
-#include "MtpDebug.h"
+#include <map>
+#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<mtpProperty>& getMtpProps();
 	std::vector<mtpProperty> mtpProp;
+	const mtpProperty& getProperty(MtpPropertyCode property);
 };
 
-// Binary Search Tree class
-class Tree {
-	Node* root;
+// A directory
+class Tree : public Node {
+	std::map<MtpObjectHandle, Node*> 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<int>* 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);
-	std::string getPath(Node* node);
-	int getMtpParentId(Node* node);
-	Node* findNodePath(std::string path, Node* node);
-	Node* getNext(Node* node);
-	int getCount();
 
-private:
-	Node* addNode(int mtpid, Node* leaf, std::string path);
-	void freeNode(Node* leaf);
-	int count;
+	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* findEntryByName(std::string name);
+	int getCount();
+	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 b5096fe..05bb5d9 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 @@
 	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 @@
 											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 @@
 		MTPE("endSendObject() failed, unlinking %s\n", path);
 		unlink(path);
 	}
+	std::map<int, MtpStorage*>::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 @@
 									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 @@
 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 @@
 		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 @@
 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<int, MtpStorage*>::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 @@
 			packet.putInt128(longValue);
 			break;
 		case MTP_TYPE_UINT128:
-			packet.putInt128(longValue);
+			packet.putUInt128(longValue);
 			break;
 		case MTP_TYPE_STR:
 			{
@@ -401,8 +409,10 @@
 				} 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 @@
 	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 @@
 		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::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 @@
 
 // ----------------------------------------------------------------------------
 
-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<int, MtpStorage*>::iterator storit;
 	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
diff --git a/mtp/node.cpp b/mtp/node.cpp
index 17047ce..1bca1d9 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);
-}
-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) {
+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);
+}
+
+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 @@
 	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 @@
 	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 @@
 	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*>( &(std::ostringstream() << mtpid) )->str();
+	std::string mtpidStr = static_cast<std::ostringstream*>( &(std::ostringstream() << handle) )->str();
 	std::string storageIDStr = static_cast<std::ostringstream*>( &(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*>( &(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);