blob: 821a788f261d9d24665f4ba3cdeaaa31fcb4c2e4 [file] [log] [blame]
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
17 */
18
19#include "MtpDebug.h"
20#include "MtpStorage.h"
21#include "MtpDataPacket.h"
22#include "MtpServer.h"
23#include "MtpEventPacket.h"
that9e0593e2014-10-08 00:01:24 +020024#include "MtpDatabase.h"
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040025
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <sys/statfs.h>
29#include <unistd.h>
30#include <dirent.h>
31#include <errno.h>
32#include <string.h>
33#include <stdio.h>
34#include <limits.h>
35#include <pthread.h>
36#include <signal.h>
37#include <sys/inotify.h>
38#include <fcntl.h>
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040039
40#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
41
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040042MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
43 const char* description, uint64_t reserveSpace,
44 bool removable, uint64_t maxFileSize, MtpServer* refserver)
45 : mStorageID(id),
46 mFilePath(filePath),
47 mDescription(description),
48 mMaxCapacity(0),
49 mMaxFileSize(maxFileSize),
50 mReserveSpace(reserveSpace),
51 mRemovable(removable),
52 mServer(refserver)
53{
54 MTPI("MtpStorage id: %d path: %s\n", id, filePath);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040055 inotify_thread = 0;
56 sendEvents = false;
that9e0593e2014-10-08 00:01:24 +020057 handleCurrentlySending = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040058 use_mutex = true;
59 if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
60 MTPE("Failed to init mtpMutex\n");
61 use_mutex = false;
62 }
63 if (pthread_mutex_init(&inMutex, NULL) != 0) {
64 MTPE("Failed to init inMutex\n");
65 use_mutex = false;
66 }
67
68}
69
70MtpStorage::~MtpStorage() {
71 if (inotify_thread) {
that9e0593e2014-10-08 00:01:24 +020072 // TODO: what does this do? manpage says it does not kill the thread
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040073 pthread_kill(inotify_thread, 0);
that9e0593e2014-10-08 00:01:24 +020074 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040075 inotify_rm_watch(inotify_fd, i->first);
76 }
77 close(inotify_fd);
78 }
79 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
80 delete i->second;
81 }
82 if (use_mutex) {
83 use_mutex = false;
84 pthread_mutex_destroy(&mtpMutex);
85 pthread_mutex_destroy(&inMutex);
86 }
87}
88
89int MtpStorage::getType() const {
90 return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
91}
92
93int MtpStorage::getFileSystemType() const {
94 return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
95}
96
97int MtpStorage::getAccessCapability() const {
98 return MTP_STORAGE_READ_WRITE;
99}
100
101uint64_t MtpStorage::getMaxCapacity() {
102 if (mMaxCapacity == 0) {
103 struct statfs stat;
104 if (statfs(getPath(), &stat))
105 return -1;
106 mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
107 }
108 return mMaxCapacity;
109}
110
111uint64_t MtpStorage::getFreeSpace() {
112 struct statfs stat;
113 if (statfs(getPath(), &stat))
114 return -1;
115 uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
116 return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
117}
118
119const char* MtpStorage::getDescription() const {
120 return (const char *)mDescription;
121}
122
123int MtpStorage::createDB() {
124 std::string mtpParent = "";
125 mtpstorageparent = getPath();
that9e0593e2014-10-08 00:01:24 +0200126 // root directory is special: handle 0, parent 0, and empty path
127 mtpmap[0] = new Tree(0, 0, "");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400128 MTPD("MtpStorage::createDB DONE\n");
129 if (use_mutex) {
130 MTPD("Starting inotify thread\n");
131 sendEvents = true;
132 inotify_thread = inotify();
133 } else {
134 MTPD("NOT starting inotify thread\n");
135 }
that9e0593e2014-10-08 00:01:24 +0200136 // for debugging and caching purposes, read the root dir already now
137 readDir(mtpstorageparent, mtpmap[0]);
138 // all other dirs are read on demand
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400139 return 0;
140}
141
142MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
that9e0593e2014-10-08 00:01:24 +0200143 MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400144 //append object id (numerical #s) of database to int array
145 MtpObjectHandleList* list = new MtpObjectHandleList();
146 if (parent == MTP_PARENT_ROOT) {
147 MTPD("parent == MTP_PARENT_ROOT\n");
that9e0593e2014-10-08 00:01:24 +0200148 parent = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400149 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400150
that9e0593e2014-10-08 00:01:24 +0200151 if (mtpmap.find(parent) == mtpmap.end()) {
152 MTPE("parent handle not found, returning empty list\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400153 return list;
154 }
155
that9e0593e2014-10-08 00:01:24 +0200156 Tree* tree = mtpmap[parent];
157 if (!tree->wasAlreadyRead())
158 {
159 std::string path = getNodePath(tree);
160 MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
161 readDir(path, tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400162 }
that9e0593e2014-10-08 00:01:24 +0200163
164 mtpmap[parent]->getmtpids(list);
165 MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400166 return list;
167}
168
169int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
170 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500171 uint64_t size = 0;
that9e0593e2014-10-08 00:01:24 +0200172 MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
173 Node* node = findNode(handle);
174 if (!node) {
175 // Item is not on this storage device
176 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400177 }
that9e0593e2014-10-08 00:01:24 +0200178
179 info.mStorageID = getStorageID();
180 MTPD("info.mStorageID: %u\n", info.mStorageID);
181 info.mParent = node->getMtpParentId();
182 MTPD("mParent: %u\n", info.mParent);
183 // TODO: do we want to lstat again here, or read from the node properties?
184 if (lstat(getNodePath(node).c_str(), &st) == 0)
185 size = st.st_size;
186 MTPD("size is: %llu\n", size);
187 info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
188 info.mDateModified = st.st_mtime;
189 if (S_ISDIR(st.st_mode)) {
190 info.mFormat = MTP_FORMAT_ASSOCIATION;
191 }
192 else {
193 info.mFormat = MTP_FORMAT_UNDEFINED;
194 }
195 info.mName = strdup(node->getName().c_str());
196 MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
197 return 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400198}
199
200MtpObjectHandle MtpStorage::beginSendObject(const char* path,
201 MtpObjectFormat format,
202 MtpObjectHandle parent,
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400203 uint64_t size,
204 time_t modified) {
that9e0593e2014-10-08 00:01:24 +0200205 MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
206 iter it = mtpmap.find(parent);
207 if (it == mtpmap.end()) {
208 MTPE("parent node not found, returning error\n");
209 return kInvalidObjectHandle;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400210 }
that9e0593e2014-10-08 00:01:24 +0200211 Tree* tree = it->second;
212
213 std::string pathstr(path);
214 size_t slashpos = pathstr.find_last_of('/');
215 if (slashpos == std::string::npos) {
216 MTPE("path has no slash, returning error\n");
217 return kInvalidObjectHandle;
218 }
219 std::string parentdir = pathstr.substr(0, slashpos);
220 std::string basename = pathstr.substr(slashpos + 1);
221 if (parent != 0 && parentdir != getNodePath(tree)) {
222 MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
223 return kInvalidObjectHandle;
224 }
225
226 MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
227 // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
228 bool isDir = format == MTP_FORMAT_ASSOCIATION;
229 Node* node = addNewNode(isDir, tree, basename);
230 handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
231
232 return node->Mtpid();
233}
234
235void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded)
236{
237 Node* node = findNode(handle);
238 if (!node)
239 return; // just ignore if this is for another storage
240
241 node->addProperties(path, mStorageID);
242 handleCurrentlySending = 0;
243 // TODO: are we supposed to send an event about an upload by the initiator?
244 if (sendEvents)
245 mServer->sendObjectAdded(node->Mtpid());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400246}
247
248int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
that9e0593e2014-10-08 00:01:24 +0200249 MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
250 Node* node = findNode(handle);
251 if (!node)
252 {
253 // Item is not on this storage device
254 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400255 }
that9e0593e2014-10-08 00:01:24 +0200256 // TODO: do we want to lstat here, or just read the info from the node?
257 struct stat st;
258 if (lstat(getNodePath(node).c_str(), &st) == 0)
259 outFileLength = st.st_size;
260 else
261 outFileLength = 0;
262 outFilePath = getNodePath(node).c_str();
263 MTPD("outFilePath: %s\n", outFilePath.string());
264 outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400265 return 0;
266}
267
that9e0593e2014-10-08 00:01:24 +0200268int MtpStorage::readDir(const std::string& path, Tree* tree)
269{
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400270 struct dirent *de;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400271 int storageID = getStorageID();
that9e0593e2014-10-08 00:01:24 +0200272 MtpObjectHandle parent = tree->Mtpid();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400273
that9e0593e2014-10-08 00:01:24 +0200274 DIR *d = opendir(path.c_str());
275 MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400276 if (d == NULL) {
that9e0593e2014-10-08 00:01:24 +0200277 MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
278 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400279 }
that9e0593e2014-10-08 00:01:24 +0200280 // TODO: for refreshing dirs: capture old entries here
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400281 while ((de = readdir(d)) != NULL) {
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500282 // Because exfat-fuse causes issues with dirent, we will use stat
283 // for some things that dirent should be able to do
that9e0593e2014-10-08 00:01:24 +0200284 std::string item = path + "/" + de->d_name;
285 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500286 if (lstat(item.c_str(), &st)) {
287 MTPE("Error running lstat on '%s'\n", item.c_str());
288 return -1;
289 }
that9e0593e2014-10-08 00:01:24 +0200290 // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
291 if (strcmp(de->d_name, ".") == 0)
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400292 continue;
that9e0593e2014-10-08 00:01:24 +0200293 if (strcmp(de->d_name, "..") == 0)
294 continue;
295 Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
296 node->addProperties(item, storageID);
297 //if (sendEvents)
298 // mServer->sendObjectAdded(node->Mtpid());
299 // sending events here makes simple-mtpfs very slow, and it is probably the wrong thing to do anyway
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400300 }
that9e0593e2014-10-08 00:01:24 +0200301 closedir(d);
302 // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
303 tree->setAlreadyRead(true);
304 addInotify(tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400305 return 0;
306}
307
that9e0593e2014-10-08 00:01:24 +0200308int MtpStorage::deleteFile(MtpObjectHandle handle) {
309 MTPD("MtpStorage::deleteFile handle: %u\n", handle);
310 Node* node = findNode(handle);
311 if (!node) {
312 // Item is not on this storage device
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400313 return -1;
314 }
that9e0593e2014-10-08 00:01:24 +0200315 MtpObjectHandle parent = node->getMtpParentId();
316 Tree* tree = mtpmap[parent];
317 if (!tree) {
318 MTPE("parent tree for handle %u not found\n", parent);
319 return -1;
320 }
321 if (node->isDir()) {
322 MTPD("deleting tree from mtpmap: %u\n", handle);
323 mtpmap.erase(handle);
324 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400325
that9e0593e2014-10-08 00:01:24 +0200326 MTPD("deleting handle: %u\n", handle);
327 tree->deleteNode(handle);
328 MTPD("deleted\n");
329 return 0;
330}
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400331
that9e0593e2014-10-08 00:01:24 +0200332void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID)
333{
334 MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
335 PropEntry pe;
336 pe.handle = node->Mtpid();
337 pe.property = property;
338
339 if (property == 0xffffffff)
340 {
341 // add all properties
342 MTPD("MtpStorage::queryNodeProperties for all properties\n");
343 std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
344 for (size_t i = 0; i < mtpprop.size(); ++i) {
345 pe.property = mtpprop[i].property;
346 pe.datatype = mtpprop[i].dataType;
347 pe.intvalue = mtpprop[i].valueInt;
348 pe.strvalue = mtpprop[i].valueStr;
349 results.push_back(pe);
350 }
351 return;
352 }
353 else if (property == 0)
354 {
355 // TODO: use groupCode
356 }
357
358 // single property
359 // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
360 switch (property) {
361// case MTP_PROPERTY_OBJECT_FORMAT:
362// pe.datatype = MTP_TYPE_UINT16;
363// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
364// break;
365
366 case MTP_PROPERTY_STORAGE_ID:
367 pe.datatype = MTP_TYPE_UINT32;
368 pe.intvalue = storageID;
369 break;
370
371 case MTP_PROPERTY_PROTECTION_STATUS:
372 pe.datatype = MTP_TYPE_UINT16;
373 pe.intvalue = 0;
374 break;
375
376 case MTP_PROPERTY_OBJECT_SIZE:
377 {
378 pe.datatype = MTP_TYPE_UINT64;
379 struct stat st;
380 pe.intvalue = 0;
381 if (lstat(getNodePath(node).c_str(), &st) == 0)
382 pe.intvalue = st.st_size;
383 break;
384 }
385
386 default:
387 {
388 const Node::mtpProperty& prop = node->getProperty(property);
389 if (prop.property != property)
390 {
391 MTPD("queryNodeProperties: unknown property %x\n", property);
392 return;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400393 }
that9e0593e2014-10-08 00:01:24 +0200394 pe.datatype = prop.dataType;
395 pe.intvalue = prop.valueInt;
396 pe.strvalue = prop.valueStr;
397 // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
398 }
399
400 }
401 results.push_back(pe);
402}
403
404int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
405 MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
406 if (groupCode != 0)
407 {
408 MTPE("getObjectPropertyList: groupCode unsupported\n");
409 return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
410 }
411 // TODO: support all the special stuff, like:
412 // handle == 0 -> all objects at the root level
413 // handle == 0xffffffff -> all objects (on all storages? how could we support that?)
414 // format == 0 -> all formats, otherwise filter by ObjectFormatCode
415 // property == 0xffffffff -> all properties except those with group code 0xffffffff
416 // if property == 0 then use groupCode
417 // groupCode == 0 -> return Specification_By_Group_Unsupported
418 // depth == 0xffffffff -> all objects incl. and below handle
419
420 std::vector<PropEntry> results;
421
422 if (handle == 0xffffffff) {
423 // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
424 } else if (handle == 0) {
425 // all objects at the root level
426 Tree* root = mtpmap[0];
427 MtpObjectHandleList list;
428 root->getmtpids(&list);
429 for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
430 Node* node = root->findNode(*it);
431 if (!node) {
432 MTPE("BUG: node not found for root entry with handle %u\n", *it);
433 break;
434 }
435 queryNodeProperties(results, node, property, groupCode, mStorageID);
436 }
437 } else {
438 // single object
439 Node* node = findNode(handle);
440 if (!node) {
441 // Item is not on this storage device
442 return -1;
443 }
444 queryNodeProperties(results, node, property, groupCode, mStorageID);
445 }
446
447 MTPD("count: %u\n", results.size());
448 packet.putUInt32(results.size());
449
450 for (size_t i = 0; i < results.size(); ++i) {
451 PropEntry& p = results[i];
452 MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
453 p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
454 p.datatype, p.intvalue);
455 packet.putUInt32(p.handle);
456 packet.putUInt16(p.property);
457 packet.putUInt16(p.datatype);
458 switch (p.datatype) {
459 case MTP_TYPE_INT8:
460 MTPD("MTP_TYPE_INT8\n");
461 packet.putInt8(p.intvalue);
462 break;
463 case MTP_TYPE_UINT8:
464 MTPD("MTP_TYPE_UINT8\n");
465 packet.putUInt8(p.intvalue);
466 break;
467 case MTP_TYPE_INT16:
468 MTPD("MTP_TYPE_INT16\n");
469 packet.putInt16(p.intvalue);
470 break;
471 case MTP_TYPE_UINT16:
472 MTPD("MTP_TYPE_UINT16\n");
473 packet.putUInt16(p.intvalue);
474 break;
475 case MTP_TYPE_INT32:
476 MTPD("MTP_TYPE_INT32\n");
477 packet.putInt32(p.intvalue);
478 break;
479 case MTP_TYPE_UINT32:
480 MTPD("MTP_TYPE_UINT32\n");
481 packet.putUInt32(p.intvalue);
482 break;
483 case MTP_TYPE_INT64:
484 MTPD("MTP_TYPE_INT64\n");
485 packet.putInt64(p.intvalue);
486 break;
487 case MTP_TYPE_UINT64:
488 MTPD("MTP_TYPE_UINT64\n");
489 packet.putUInt64(p.intvalue);
490 break;
491 case MTP_TYPE_INT128:
492 MTPD("MTP_TYPE_INT128\n");
493 packet.putInt128(p.intvalue);
494 break;
495 case MTP_TYPE_UINT128:
496 MTPD("MTP_TYPE_UINT128\n");
497 packet.putUInt128(p.intvalue);
498 break;
499 case MTP_TYPE_STR:
500 MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str());
501 packet.putString(p.strvalue.c_str());
502 break;
503 default:
504 MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
505 break;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400506 }
507 }
508 return 0;
509}
510
511int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
that9e0593e2014-10-08 00:01:24 +0200512 MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400513 if (handle == MTP_PARENT_ROOT) {
514 MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
515 return -1;
516 } else {
517 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200518 Node* node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400519 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200520 std::string oldName = getNodePath(node);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400521 std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
522 std::string newFullName = parentdir + "/" + newName;
523 MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
524 if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
that9e0593e2014-10-08 00:01:24 +0200525 node->rename(newName);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400526 return 0;
527 } else {
that9e0593e2014-10-08 00:01:24 +0200528 MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400529 return -1;
530 }
531 }
532 }
533 }
Ethan Yonker6f49e112014-09-03 21:42:49 -0500534 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400535 return -1;
536}
537
that9e0593e2014-10-08 00:01:24 +0200538int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400539 Node *node;
540 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200541 node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400542 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200543 const Node::mtpProperty& prop = node->getProperty(property);
544 if (prop.property != property) {
545 MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
546 return -1;
547 }
548 pe.datatype = prop.dataType;
549 pe.intvalue = prop.valueInt;
550 pe.strvalue = prop.valueStr;
551 pe.handle = handle;
552 pe.property = property;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400553 return 0;
554 }
555 }
Ethan Yonker5e083dc2014-09-03 14:46:41 -0500556 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400557 return -1;
558}
that9e0593e2014-10-08 00:01:24 +0200559
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400560pthread_t MtpStorage::inotify(void) {
561 pthread_t thread;
562 ThreadPtr inotifyptr = &MtpStorage::inotify_t;
563 PThreadPtr p = *(PThreadPtr*)&inotifyptr;
564 pthread_create(&thread, NULL, p, this);
565 return thread;
566}
567
that9e0593e2014-10-08 00:01:24 +0200568int MtpStorage::addInotify(Tree* tree) {
569 std::string path = getNodePath(tree);
570 MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
571 int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
572 if (wd < 0) {
573 MTPE("inotify_add_watch failed: %s\n", strerror(errno));
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400574 return -1;
575 }
that9e0593e2014-10-08 00:01:24 +0200576 inotifymap[wd] = tree;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400577 return 0;
578}
579
that9e0593e2014-10-08 00:01:24 +0200580void MtpStorage::handleInotifyEvent(struct inotify_event* event)
581{
582 std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
583 if (it == inotifymap.end()) {
584 MTPE("Unable to locate inotify_wd: %i\n", event->wd);
585 return;
586 }
587 Tree* tree = it->second;
588 MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
589 Node* node = tree->findEntryByName(basename(event->name));
590 if (node && node->Mtpid() == handleCurrentlySending) {
591 MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
592 return;
593 }
594 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
595 if (event->mask & IN_ISDIR) {
596 MTPD("inotify_t create is dir\n");
597 } else {
598 MTPD("inotify_t create is file\n");
599 }
600 if (node == NULL) {
601 node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
602 std::string item = getNodePath(tree) + "/" + event->name;
603 node->addProperties(item, getStorageID());
604 mServer->sendObjectAdded(node->Mtpid());
605 } else {
606 MTPD("inotify_t item already exists.\n");
607 }
608 if (event->mask & IN_ISDIR) {
609 // TODO: do we need to do anything here? probably not until someone reads from the dir...
610 }
611 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
612 if (event->mask & IN_ISDIR) {
613 MTPD("inotify_t Directory %s deleted\n", event->name);
614 } else {
615 MTPD("inotify_t File %s deleted\n", event->name);
616 }
617 if (node)
618 {
619 if (event->mask & IN_ISDIR) {
620 for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
621 if (it->second == node) {
622 inotify_rm_watch(inotify_fd, it->first);
623 MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
624 inotifymap.erase(it->first);
625 break;
626 }
627
628 }
629 }
630 MtpObjectHandle handle = node->Mtpid();
631 deleteFile(handle);
632 mServer->sendObjectRemoved(handle);
633 } else {
634 MTPD("inotify_t already removed.\n");
635 }
636 } else if (event->mask & IN_MODIFY) {
637 MTPD("inotify_t item %s modified.\n", event->name);
638 if (node != NULL) {
639 uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
640 struct stat st;
641 uint64_t new_size = 0;
642 if (lstat(getNodePath(node).c_str(), &st) == 0)
643 new_size = (uint64_t)st.st_size;
644 if (orig_size != new_size) {
645 MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
646 node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
647 mServer->sendObjectUpdated(node->Mtpid());
648 }
649 } else {
650 MTPE("inotify_t modified item not found\n");
651 }
652 } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
653 // TODO: is this always already handled by IN_DELETE for the parent dir?
654 }
655}
656
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400657int MtpStorage::inotify_t(void) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400658 #define EVENT_SIZE ( sizeof(struct inotify_event) )
659 #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
660 char buf[EVENT_BUF_LEN];
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400661
that9e0593e2014-10-08 00:01:24 +0200662 MTPD("inotify thread: inotify_init\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400663 inotify_fd = inotify_init();
664
that9e0593e2014-10-08 00:01:24 +0200665 if (inotify_fd < 0) {
666 MTPE("Can't run inotify for mtp server: %s\n", strerror(errno));
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400667 return -1;
668 }
669
670 while (true) {
that9e0593e2014-10-08 00:01:24 +0200671 int i = 0;
672 int len = read(inotify_fd, buf, EVENT_BUF_LEN);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400673
674 if (len < 0) {
that9e0593e2014-10-08 00:01:24 +0200675 if (errno == EINTR)
676 continue;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400677 MTPE("inotify_t Can't read inotify events\n");
678 }
679
680 while (i < len) {
that9e0593e2014-10-08 00:01:24 +0200681 struct inotify_event *event = (struct inotify_event *) &buf[i];
682 if (event->len) {
683 MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
684 lockMutex(1);
685 handleInotifyEvent(event);
686 unlockMutex(1);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400687 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400688 i += EVENT_SIZE + event->len;
689 }
690 }
691
that9e0593e2014-10-08 00:01:24 +0200692 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400693 inotify_rm_watch(inotify_fd, i->first);
694 }
695 close(inotify_fd);
696 return 0;
697}
698
that9e0593e2014-10-08 00:01:24 +0200699Node* MtpStorage::findNodeByPath(const std::string& path) {
700 MTPD("findNodeByPath: %s\n", path.c_str());
701 std::string match = path.substr(0, mtpstorageparent.size());
702 if (match != mtpstorageparent) {
703 // not on this device
704 MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str());
705 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400706 }
that9e0593e2014-10-08 00:01:24 +0200707
708 // TODO: fix and test this
709 std::string p = path.substr(mtpstorageparent.size()+1); // cut off "/" after storage root too
710 Tree* tree = mtpmap[0]; // start at storage root
711
712 Node* node = NULL;
713 while (!p.empty()) {
714 size_t slashpos = p.find('/');
715 std::string e;
716 if (slashpos != std::string::npos) {
717 e = p;
718 p.clear();
719 } else {
720 e = p.substr(0, slashpos);
721 p = p.substr(slashpos + 1);
722 }
723 MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str());
724 node = tree->findEntryByName(e);
725 if (!node) {
726 MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str());
727 return NULL;
728 }
729 if (node->isDir())
730 tree = static_cast<Tree*>(node);
731 else if (!p.empty()) {
732 MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node);
733 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400734 }
735 }
that9e0593e2014-10-08 00:01:24 +0200736 MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str());
737 return node;
738}
739
740Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
741{
742 // global counter for new object handles
743 static MtpObjectHandle mtpid = 0;
744
745 ++mtpid;
746 MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
747 MtpObjectHandle parent = tree->Mtpid();
748 MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
749 Node* node;
750 if (isDir)
751 node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
752 else
753 node = new Node(mtpid, parent, name);
754 tree->addEntry(node);
755 return node;
756}
757
758Node* MtpStorage::findNode(MtpObjectHandle handle) {
759 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
760 Node* node = i->second->findNode(handle);
761 if (node != NULL) {
762 MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
763 if (node->Mtpid() != handle)
764 {
765 MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
766 }
767 return node;
768 }
769 }
770 // Item is not on this storage device
that5823d482014-10-13 00:15:05 +0200771 MTPD("MtpStorage::findNode: no node found for handle %u on storage %u, searched %u trees\n", handle, mStorageID, mtpmap.size());
that9e0593e2014-10-08 00:01:24 +0200772 return NULL;
773}
774
775std::string MtpStorage::getNodePath(Node* node) {
776 std::string path;
777 MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
778 while (node)
779 {
780 path = "/" + node->getName() + path;
781 MtpObjectHandle parent = node->getMtpParentId();
782 if (parent == 0) // root
783 break;
784 node = findNode(parent);
785 }
786 path = mtpstorageparent + path;
787 MTPD("getNodePath: path %s\n", path.c_str());
788 return path;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400789}
790
791void MtpStorage::lockMutex(int thread_type) {
792 if (!use_mutex)
793 return; // mutex is disabled
794 if (thread_type) {
795 // inotify thread
796 pthread_mutex_lock(&inMutex);
797 while (pthread_mutex_trylock(&mtpMutex)) {
798 pthread_mutex_unlock(&inMutex);
799 usleep(32000);
800 pthread_mutex_lock(&inMutex);
801 }
802 } else {
803 // main mtp thread
804 pthread_mutex_lock(&mtpMutex);
805 while (pthread_mutex_trylock(&inMutex)) {
806 pthread_mutex_unlock(&mtpMutex);
807 usleep(13000);
808 pthread_mutex_lock(&mtpMutex);
809 }
810 }
811}
812
813void MtpStorage::unlockMutex(int thread_type) {
814 if (!use_mutex)
815 return; // mutex is disabled
816 pthread_mutex_unlock(&inMutex);
817 pthread_mutex_unlock(&mtpMutex);
818}