blob: 319be094bf5e70642d671478faee8846a96d5f1b [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;
Ethan Yonkercdc3ef52014-11-25 10:40:04 -060056 inotify_fd = -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040057 sendEvents = false;
that9e0593e2014-10-08 00:01:24 +020058 handleCurrentlySending = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040059 use_mutex = true;
60 if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
61 MTPE("Failed to init mtpMutex\n");
62 use_mutex = false;
63 }
64 if (pthread_mutex_init(&inMutex, NULL) != 0) {
65 MTPE("Failed to init inMutex\n");
66 use_mutex = false;
67 }
68
69}
70
71MtpStorage::~MtpStorage() {
72 if (inotify_thread) {
that9e0593e2014-10-08 00:01:24 +020073 // TODO: what does this do? manpage says it does not kill the thread
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040074 pthread_kill(inotify_thread, 0);
that9e0593e2014-10-08 00:01:24 +020075 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040076 inotify_rm_watch(inotify_fd, i->first);
77 }
78 close(inotify_fd);
79 }
80 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
81 delete i->second;
82 }
83 if (use_mutex) {
84 use_mutex = false;
85 pthread_mutex_destroy(&mtpMutex);
86 pthread_mutex_destroy(&inMutex);
87 }
88}
89
90int MtpStorage::getType() const {
91 return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
92}
93
94int MtpStorage::getFileSystemType() const {
95 return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
96}
97
98int MtpStorage::getAccessCapability() const {
99 return MTP_STORAGE_READ_WRITE;
100}
101
102uint64_t MtpStorage::getMaxCapacity() {
103 if (mMaxCapacity == 0) {
104 struct statfs stat;
105 if (statfs(getPath(), &stat))
106 return -1;
107 mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
108 }
109 return mMaxCapacity;
110}
111
112uint64_t MtpStorage::getFreeSpace() {
113 struct statfs stat;
114 if (statfs(getPath(), &stat))
115 return -1;
116 uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
117 return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
118}
119
120const char* MtpStorage::getDescription() const {
121 return (const char *)mDescription;
122}
123
124int MtpStorage::createDB() {
125 std::string mtpParent = "";
126 mtpstorageparent = getPath();
that9e0593e2014-10-08 00:01:24 +0200127 // root directory is special: handle 0, parent 0, and empty path
128 mtpmap[0] = new Tree(0, 0, "");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400129 MTPD("MtpStorage::createDB DONE\n");
130 if (use_mutex) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400131 sendEvents = true;
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600132 MTPD("inotify_init\n");
133 inotify_fd = inotify_init();
134 if (inotify_fd < 0) {
135 MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
136 } else {
137 MTPD("Starting inotify thread\n");
138 inotify_thread = inotify();
139 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400140 } else {
141 MTPD("NOT starting inotify thread\n");
142 }
that9e0593e2014-10-08 00:01:24 +0200143 // for debugging and caching purposes, read the root dir already now
144 readDir(mtpstorageparent, mtpmap[0]);
145 // all other dirs are read on demand
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400146 return 0;
147}
148
149MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
that9e0593e2014-10-08 00:01:24 +0200150 MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400151 //append object id (numerical #s) of database to int array
152 MtpObjectHandleList* list = new MtpObjectHandleList();
153 if (parent == MTP_PARENT_ROOT) {
154 MTPD("parent == MTP_PARENT_ROOT\n");
that9e0593e2014-10-08 00:01:24 +0200155 parent = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400156 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400157
that9e0593e2014-10-08 00:01:24 +0200158 if (mtpmap.find(parent) == mtpmap.end()) {
159 MTPE("parent handle not found, returning empty list\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400160 return list;
161 }
162
that9e0593e2014-10-08 00:01:24 +0200163 Tree* tree = mtpmap[parent];
164 if (!tree->wasAlreadyRead())
165 {
166 std::string path = getNodePath(tree);
167 MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
168 readDir(path, tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400169 }
that9e0593e2014-10-08 00:01:24 +0200170
171 mtpmap[parent]->getmtpids(list);
172 MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400173 return list;
174}
175
176int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
177 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500178 uint64_t size = 0;
that9e0593e2014-10-08 00:01:24 +0200179 MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
180 Node* node = findNode(handle);
181 if (!node) {
182 // Item is not on this storage device
183 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400184 }
that9e0593e2014-10-08 00:01:24 +0200185
186 info.mStorageID = getStorageID();
187 MTPD("info.mStorageID: %u\n", info.mStorageID);
188 info.mParent = node->getMtpParentId();
189 MTPD("mParent: %u\n", info.mParent);
190 // TODO: do we want to lstat again here, or read from the node properties?
191 if (lstat(getNodePath(node).c_str(), &st) == 0)
192 size = st.st_size;
193 MTPD("size is: %llu\n", size);
194 info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
195 info.mDateModified = st.st_mtime;
196 if (S_ISDIR(st.st_mode)) {
197 info.mFormat = MTP_FORMAT_ASSOCIATION;
198 }
199 else {
200 info.mFormat = MTP_FORMAT_UNDEFINED;
201 }
202 info.mName = strdup(node->getName().c_str());
203 MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
204 return 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400205}
206
207MtpObjectHandle MtpStorage::beginSendObject(const char* path,
208 MtpObjectFormat format,
209 MtpObjectHandle parent,
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400210 uint64_t size,
211 time_t modified) {
that9e0593e2014-10-08 00:01:24 +0200212 MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
213 iter it = mtpmap.find(parent);
214 if (it == mtpmap.end()) {
215 MTPE("parent node not found, returning error\n");
216 return kInvalidObjectHandle;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400217 }
that9e0593e2014-10-08 00:01:24 +0200218 Tree* tree = it->second;
219
220 std::string pathstr(path);
221 size_t slashpos = pathstr.find_last_of('/');
222 if (slashpos == std::string::npos) {
223 MTPE("path has no slash, returning error\n");
224 return kInvalidObjectHandle;
225 }
226 std::string parentdir = pathstr.substr(0, slashpos);
227 std::string basename = pathstr.substr(slashpos + 1);
228 if (parent != 0 && parentdir != getNodePath(tree)) {
229 MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
230 return kInvalidObjectHandle;
231 }
232
233 MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
234 // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
235 bool isDir = format == MTP_FORMAT_ASSOCIATION;
236 Node* node = addNewNode(isDir, tree, basename);
237 handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
238
239 return node->Mtpid();
240}
241
242void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded)
243{
244 Node* node = findNode(handle);
245 if (!node)
246 return; // just ignore if this is for another storage
247
248 node->addProperties(path, mStorageID);
249 handleCurrentlySending = 0;
250 // TODO: are we supposed to send an event about an upload by the initiator?
251 if (sendEvents)
252 mServer->sendObjectAdded(node->Mtpid());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400253}
254
255int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
that9e0593e2014-10-08 00:01:24 +0200256 MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
257 Node* node = findNode(handle);
258 if (!node)
259 {
260 // Item is not on this storage device
261 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400262 }
that9e0593e2014-10-08 00:01:24 +0200263 // TODO: do we want to lstat here, or just read the info from the node?
264 struct stat st;
265 if (lstat(getNodePath(node).c_str(), &st) == 0)
266 outFileLength = st.st_size;
267 else
268 outFileLength = 0;
269 outFilePath = getNodePath(node).c_str();
270 MTPD("outFilePath: %s\n", outFilePath.string());
271 outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400272 return 0;
273}
274
that9e0593e2014-10-08 00:01:24 +0200275int MtpStorage::readDir(const std::string& path, Tree* tree)
276{
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400277 struct dirent *de;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400278 int storageID = getStorageID();
that9e0593e2014-10-08 00:01:24 +0200279 MtpObjectHandle parent = tree->Mtpid();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400280
that9e0593e2014-10-08 00:01:24 +0200281 DIR *d = opendir(path.c_str());
282 MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400283 if (d == NULL) {
that9e0593e2014-10-08 00:01:24 +0200284 MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
285 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400286 }
that9e0593e2014-10-08 00:01:24 +0200287 // TODO: for refreshing dirs: capture old entries here
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400288 while ((de = readdir(d)) != NULL) {
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500289 // Because exfat-fuse causes issues with dirent, we will use stat
290 // for some things that dirent should be able to do
that9e0593e2014-10-08 00:01:24 +0200291 std::string item = path + "/" + de->d_name;
292 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500293 if (lstat(item.c_str(), &st)) {
294 MTPE("Error running lstat on '%s'\n", item.c_str());
295 return -1;
296 }
that9e0593e2014-10-08 00:01:24 +0200297 // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
298 if (strcmp(de->d_name, ".") == 0)
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400299 continue;
that9e0593e2014-10-08 00:01:24 +0200300 if (strcmp(de->d_name, "..") == 0)
301 continue;
302 Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
303 node->addProperties(item, storageID);
304 //if (sendEvents)
305 // mServer->sendObjectAdded(node->Mtpid());
306 // 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 -0400307 }
that9e0593e2014-10-08 00:01:24 +0200308 closedir(d);
309 // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
310 tree->setAlreadyRead(true);
311 addInotify(tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400312 return 0;
313}
314
that9e0593e2014-10-08 00:01:24 +0200315int MtpStorage::deleteFile(MtpObjectHandle handle) {
316 MTPD("MtpStorage::deleteFile handle: %u\n", handle);
317 Node* node = findNode(handle);
318 if (!node) {
319 // Item is not on this storage device
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400320 return -1;
321 }
that9e0593e2014-10-08 00:01:24 +0200322 MtpObjectHandle parent = node->getMtpParentId();
323 Tree* tree = mtpmap[parent];
324 if (!tree) {
325 MTPE("parent tree for handle %u not found\n", parent);
326 return -1;
327 }
328 if (node->isDir()) {
329 MTPD("deleting tree from mtpmap: %u\n", handle);
330 mtpmap.erase(handle);
331 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400332
that9e0593e2014-10-08 00:01:24 +0200333 MTPD("deleting handle: %u\n", handle);
334 tree->deleteNode(handle);
335 MTPD("deleted\n");
336 return 0;
337}
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400338
that9e0593e2014-10-08 00:01:24 +0200339void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID)
340{
341 MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
342 PropEntry pe;
343 pe.handle = node->Mtpid();
344 pe.property = property;
345
346 if (property == 0xffffffff)
347 {
348 // add all properties
349 MTPD("MtpStorage::queryNodeProperties for all properties\n");
350 std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
351 for (size_t i = 0; i < mtpprop.size(); ++i) {
352 pe.property = mtpprop[i].property;
353 pe.datatype = mtpprop[i].dataType;
354 pe.intvalue = mtpprop[i].valueInt;
355 pe.strvalue = mtpprop[i].valueStr;
356 results.push_back(pe);
357 }
358 return;
359 }
360 else if (property == 0)
361 {
362 // TODO: use groupCode
363 }
364
365 // single property
366 // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
367 switch (property) {
368// case MTP_PROPERTY_OBJECT_FORMAT:
369// pe.datatype = MTP_TYPE_UINT16;
370// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
371// break;
372
373 case MTP_PROPERTY_STORAGE_ID:
374 pe.datatype = MTP_TYPE_UINT32;
375 pe.intvalue = storageID;
376 break;
377
378 case MTP_PROPERTY_PROTECTION_STATUS:
379 pe.datatype = MTP_TYPE_UINT16;
380 pe.intvalue = 0;
381 break;
382
383 case MTP_PROPERTY_OBJECT_SIZE:
384 {
385 pe.datatype = MTP_TYPE_UINT64;
386 struct stat st;
387 pe.intvalue = 0;
388 if (lstat(getNodePath(node).c_str(), &st) == 0)
389 pe.intvalue = st.st_size;
390 break;
391 }
392
393 default:
394 {
395 const Node::mtpProperty& prop = node->getProperty(property);
396 if (prop.property != property)
397 {
398 MTPD("queryNodeProperties: unknown property %x\n", property);
399 return;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400400 }
that9e0593e2014-10-08 00:01:24 +0200401 pe.datatype = prop.dataType;
402 pe.intvalue = prop.valueInt;
403 pe.strvalue = prop.valueStr;
404 // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
405 }
406
407 }
408 results.push_back(pe);
409}
410
411int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
412 MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
413 if (groupCode != 0)
414 {
415 MTPE("getObjectPropertyList: groupCode unsupported\n");
416 return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
417 }
418 // TODO: support all the special stuff, like:
419 // handle == 0 -> all objects at the root level
420 // handle == 0xffffffff -> all objects (on all storages? how could we support that?)
421 // format == 0 -> all formats, otherwise filter by ObjectFormatCode
422 // property == 0xffffffff -> all properties except those with group code 0xffffffff
423 // if property == 0 then use groupCode
424 // groupCode == 0 -> return Specification_By_Group_Unsupported
425 // depth == 0xffffffff -> all objects incl. and below handle
426
427 std::vector<PropEntry> results;
428
429 if (handle == 0xffffffff) {
430 // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
431 } else if (handle == 0) {
432 // all objects at the root level
433 Tree* root = mtpmap[0];
434 MtpObjectHandleList list;
435 root->getmtpids(&list);
436 for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
437 Node* node = root->findNode(*it);
438 if (!node) {
439 MTPE("BUG: node not found for root entry with handle %u\n", *it);
440 break;
441 }
442 queryNodeProperties(results, node, property, groupCode, mStorageID);
443 }
444 } else {
445 // single object
446 Node* node = findNode(handle);
447 if (!node) {
448 // Item is not on this storage device
449 return -1;
450 }
451 queryNodeProperties(results, node, property, groupCode, mStorageID);
452 }
453
454 MTPD("count: %u\n", results.size());
455 packet.putUInt32(results.size());
456
457 for (size_t i = 0; i < results.size(); ++i) {
458 PropEntry& p = results[i];
459 MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
460 p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
461 p.datatype, p.intvalue);
462 packet.putUInt32(p.handle);
463 packet.putUInt16(p.property);
464 packet.putUInt16(p.datatype);
465 switch (p.datatype) {
466 case MTP_TYPE_INT8:
467 MTPD("MTP_TYPE_INT8\n");
468 packet.putInt8(p.intvalue);
469 break;
470 case MTP_TYPE_UINT8:
471 MTPD("MTP_TYPE_UINT8\n");
472 packet.putUInt8(p.intvalue);
473 break;
474 case MTP_TYPE_INT16:
475 MTPD("MTP_TYPE_INT16\n");
476 packet.putInt16(p.intvalue);
477 break;
478 case MTP_TYPE_UINT16:
479 MTPD("MTP_TYPE_UINT16\n");
480 packet.putUInt16(p.intvalue);
481 break;
482 case MTP_TYPE_INT32:
483 MTPD("MTP_TYPE_INT32\n");
484 packet.putInt32(p.intvalue);
485 break;
486 case MTP_TYPE_UINT32:
487 MTPD("MTP_TYPE_UINT32\n");
488 packet.putUInt32(p.intvalue);
489 break;
490 case MTP_TYPE_INT64:
491 MTPD("MTP_TYPE_INT64\n");
492 packet.putInt64(p.intvalue);
493 break;
494 case MTP_TYPE_UINT64:
495 MTPD("MTP_TYPE_UINT64\n");
496 packet.putUInt64(p.intvalue);
497 break;
498 case MTP_TYPE_INT128:
499 MTPD("MTP_TYPE_INT128\n");
500 packet.putInt128(p.intvalue);
501 break;
502 case MTP_TYPE_UINT128:
503 MTPD("MTP_TYPE_UINT128\n");
504 packet.putUInt128(p.intvalue);
505 break;
506 case MTP_TYPE_STR:
507 MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str());
508 packet.putString(p.strvalue.c_str());
509 break;
510 default:
511 MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
512 break;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400513 }
514 }
515 return 0;
516}
517
518int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
that9e0593e2014-10-08 00:01:24 +0200519 MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400520 if (handle == MTP_PARENT_ROOT) {
521 MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
522 return -1;
523 } else {
524 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200525 Node* node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400526 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200527 std::string oldName = getNodePath(node);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400528 std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
529 std::string newFullName = parentdir + "/" + newName;
530 MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
531 if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
that9e0593e2014-10-08 00:01:24 +0200532 node->rename(newName);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400533 return 0;
534 } else {
that9e0593e2014-10-08 00:01:24 +0200535 MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400536 return -1;
537 }
538 }
539 }
540 }
Ethan Yonker6f49e112014-09-03 21:42:49 -0500541 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400542 return -1;
543}
544
that9e0593e2014-10-08 00:01:24 +0200545int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400546 Node *node;
547 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200548 node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400549 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200550 const Node::mtpProperty& prop = node->getProperty(property);
551 if (prop.property != property) {
552 MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
553 return -1;
554 }
555 pe.datatype = prop.dataType;
556 pe.intvalue = prop.valueInt;
557 pe.strvalue = prop.valueStr;
558 pe.handle = handle;
559 pe.property = property;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400560 return 0;
561 }
562 }
Ethan Yonker5e083dc2014-09-03 14:46:41 -0500563 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400564 return -1;
565}
that9e0593e2014-10-08 00:01:24 +0200566
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400567pthread_t MtpStorage::inotify(void) {
568 pthread_t thread;
569 ThreadPtr inotifyptr = &MtpStorage::inotify_t;
570 PThreadPtr p = *(PThreadPtr*)&inotifyptr;
571 pthread_create(&thread, NULL, p, this);
572 return thread;
573}
574
that9e0593e2014-10-08 00:01:24 +0200575int MtpStorage::addInotify(Tree* tree) {
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600576 if (inotify_fd < 0) {
577 MTPE("inotify_fd not set or error: %i\n", inotify_fd);
578 return -1;
579 }
that9e0593e2014-10-08 00:01:24 +0200580 std::string path = getNodePath(tree);
581 MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
582 int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
583 if (wd < 0) {
584 MTPE("inotify_add_watch failed: %s\n", strerror(errno));
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400585 return -1;
586 }
that9e0593e2014-10-08 00:01:24 +0200587 inotifymap[wd] = tree;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400588 return 0;
589}
590
that9e0593e2014-10-08 00:01:24 +0200591void MtpStorage::handleInotifyEvent(struct inotify_event* event)
592{
593 std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
594 if (it == inotifymap.end()) {
595 MTPE("Unable to locate inotify_wd: %i\n", event->wd);
596 return;
597 }
598 Tree* tree = it->second;
599 MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
600 Node* node = tree->findEntryByName(basename(event->name));
601 if (node && node->Mtpid() == handleCurrentlySending) {
602 MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
603 return;
604 }
605 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
606 if (event->mask & IN_ISDIR) {
607 MTPD("inotify_t create is dir\n");
608 } else {
609 MTPD("inotify_t create is file\n");
610 }
611 if (node == NULL) {
612 node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
613 std::string item = getNodePath(tree) + "/" + event->name;
614 node->addProperties(item, getStorageID());
615 mServer->sendObjectAdded(node->Mtpid());
616 } else {
617 MTPD("inotify_t item already exists.\n");
618 }
619 if (event->mask & IN_ISDIR) {
620 // TODO: do we need to do anything here? probably not until someone reads from the dir...
621 }
622 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
623 if (event->mask & IN_ISDIR) {
624 MTPD("inotify_t Directory %s deleted\n", event->name);
625 } else {
626 MTPD("inotify_t File %s deleted\n", event->name);
627 }
628 if (node)
629 {
630 if (event->mask & IN_ISDIR) {
631 for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
632 if (it->second == node) {
633 inotify_rm_watch(inotify_fd, it->first);
634 MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
635 inotifymap.erase(it->first);
636 break;
637 }
638
639 }
640 }
641 MtpObjectHandle handle = node->Mtpid();
642 deleteFile(handle);
643 mServer->sendObjectRemoved(handle);
644 } else {
645 MTPD("inotify_t already removed.\n");
646 }
647 } else if (event->mask & IN_MODIFY) {
648 MTPD("inotify_t item %s modified.\n", event->name);
649 if (node != NULL) {
650 uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
651 struct stat st;
652 uint64_t new_size = 0;
653 if (lstat(getNodePath(node).c_str(), &st) == 0)
654 new_size = (uint64_t)st.st_size;
655 if (orig_size != new_size) {
656 MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
657 node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
658 mServer->sendObjectUpdated(node->Mtpid());
659 }
660 } else {
661 MTPE("inotify_t modified item not found\n");
662 }
663 } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
664 // TODO: is this always already handled by IN_DELETE for the parent dir?
665 }
666}
667
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400668int MtpStorage::inotify_t(void) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400669 #define EVENT_SIZE ( sizeof(struct inotify_event) )
670 #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
671 char buf[EVENT_BUF_LEN];
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400672
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600673 MTPD("inotify thread starting.\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400674
675 while (true) {
that9e0593e2014-10-08 00:01:24 +0200676 int i = 0;
677 int len = read(inotify_fd, buf, EVENT_BUF_LEN);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400678
679 if (len < 0) {
that9e0593e2014-10-08 00:01:24 +0200680 if (errno == EINTR)
681 continue;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400682 MTPE("inotify_t Can't read inotify events\n");
683 }
684
685 while (i < len) {
that9e0593e2014-10-08 00:01:24 +0200686 struct inotify_event *event = (struct inotify_event *) &buf[i];
687 if (event->len) {
688 MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
689 lockMutex(1);
690 handleInotifyEvent(event);
691 unlockMutex(1);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400692 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400693 i += EVENT_SIZE + event->len;
694 }
695 }
696
that9e0593e2014-10-08 00:01:24 +0200697 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400698 inotify_rm_watch(inotify_fd, i->first);
699 }
700 close(inotify_fd);
701 return 0;
702}
703
that9e0593e2014-10-08 00:01:24 +0200704Node* MtpStorage::findNodeByPath(const std::string& path) {
705 MTPD("findNodeByPath: %s\n", path.c_str());
706 std::string match = path.substr(0, mtpstorageparent.size());
707 if (match != mtpstorageparent) {
708 // not on this device
709 MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str());
710 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400711 }
that9e0593e2014-10-08 00:01:24 +0200712
713 // TODO: fix and test this
714 std::string p = path.substr(mtpstorageparent.size()+1); // cut off "/" after storage root too
715 Tree* tree = mtpmap[0]; // start at storage root
716
717 Node* node = NULL;
718 while (!p.empty()) {
719 size_t slashpos = p.find('/');
720 std::string e;
721 if (slashpos != std::string::npos) {
722 e = p;
723 p.clear();
724 } else {
725 e = p.substr(0, slashpos);
726 p = p.substr(slashpos + 1);
727 }
728 MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str());
729 node = tree->findEntryByName(e);
730 if (!node) {
731 MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str());
732 return NULL;
733 }
734 if (node->isDir())
735 tree = static_cast<Tree*>(node);
736 else if (!p.empty()) {
737 MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node);
738 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400739 }
740 }
that9e0593e2014-10-08 00:01:24 +0200741 MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str());
742 return node;
743}
744
745Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
746{
747 // global counter for new object handles
748 static MtpObjectHandle mtpid = 0;
749
750 ++mtpid;
751 MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
752 MtpObjectHandle parent = tree->Mtpid();
753 MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
754 Node* node;
755 if (isDir)
756 node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
757 else
758 node = new Node(mtpid, parent, name);
759 tree->addEntry(node);
760 return node;
761}
762
763Node* MtpStorage::findNode(MtpObjectHandle handle) {
764 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
765 Node* node = i->second->findNode(handle);
766 if (node != NULL) {
767 MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
768 if (node->Mtpid() != handle)
769 {
770 MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
771 }
772 return node;
773 }
774 }
775 // Item is not on this storage device
that5823d482014-10-13 00:15:05 +0200776 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 +0200777 return NULL;
778}
779
780std::string MtpStorage::getNodePath(Node* node) {
781 std::string path;
782 MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
783 while (node)
784 {
785 path = "/" + node->getName() + path;
786 MtpObjectHandle parent = node->getMtpParentId();
787 if (parent == 0) // root
788 break;
789 node = findNode(parent);
790 }
791 path = mtpstorageparent + path;
792 MTPD("getNodePath: path %s\n", path.c_str());
793 return path;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400794}
795
796void MtpStorage::lockMutex(int thread_type) {
797 if (!use_mutex)
798 return; // mutex is disabled
799 if (thread_type) {
800 // inotify thread
801 pthread_mutex_lock(&inMutex);
802 while (pthread_mutex_trylock(&mtpMutex)) {
803 pthread_mutex_unlock(&inMutex);
804 usleep(32000);
805 pthread_mutex_lock(&inMutex);
806 }
807 } else {
808 // main mtp thread
809 pthread_mutex_lock(&mtpMutex);
810 while (pthread_mutex_trylock(&inMutex)) {
811 pthread_mutex_unlock(&mtpMutex);
812 usleep(13000);
813 pthread_mutex_lock(&mtpMutex);
814 }
815 }
816}
817
818void MtpStorage::unlockMutex(int thread_type) {
819 if (!use_mutex)
820 return; // mutex is disabled
821 pthread_mutex_unlock(&inMutex);
822 pthread_mutex_unlock(&mtpMutex);
823}