blob: 8c67b5bd6b9ca21d1dc2eb68f38fc9dd0bb984dc [file] [log] [blame]
bigbiff bigbiffaf32bb92018-12-18 18:39:53 -05001/*
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
17#define LOG_TAG "MtpStorage"
18
19#include "MtpDebug.h"
20#include "MtpStorage.h"
21#include "btree.hpp"
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/statfs.h>
26#include <unistd.h>
27#include <dirent.h>
28#include <errno.h>
29#include <string.h>
30#include <stdio.h>
31#include <limits.h>
32#include <iterator>
33#include <sys/inotify.h>
34
35#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
36
37MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
38 const char* description, bool removable, uint64_t maxFileSize, MtpServer* refserver)
39 : mStorageID(id),
40 mFilePath(filePath),
41 mDescription(description),
42 mMaxCapacity(0),
43 mMaxFileSize(maxFileSize),
44 mRemovable(removable),
45 mServer(refserver)
46{
47 MTPD("MtpStorage id: %d path: %s\n", id, filePath);
48 inotify_thread = 0;
49 inotify_fd = -1;
50 // Threading has not started yet so we should be safe to set these directly instead of using atomics
51 inotify_thread_kill.set_value(0);
52 sendEvents = false;
53 handleCurrentlySending = 0;
54 use_mutex = true;
55 if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
56 MTPE("Failed to init mtpMutex\n");
57 use_mutex = false;
58 }
59 if (pthread_mutex_init(&inMutex, NULL) != 0) {
60 MTPE("Failed to init inMutex\n");
61 pthread_mutex_destroy(&mtpMutex);
62 use_mutex = false;
63 }
64}
65
66MtpStorage::~MtpStorage() {
67 if (inotify_thread) {
68 inotify_thread_kill.set_value(1);
69 MTPD("joining inotify_thread after sending the kill notification.\n");
70 pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here
71 inotify_thread = 0;
72 MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n");
73 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
74 inotify_rm_watch(inotify_fd, i->first);
75 }
76 close(inotify_fd);
77 inotifymap.clear();
78 }
79 // Deleting the root tree causes a cascade in btree.cpp that ends up
80 // deleting all of the trees and nodes.
81 delete mtpmap[0];
82 mtpmap.clear();
83 if (use_mutex) {
84 use_mutex = false;
85 MTPD("~MtpStorage destroying mutexes\n");
86 pthread_mutex_destroy(&mtpMutex);
87 pthread_mutex_destroy(&inMutex);
88 }
89
90}
91
92int MtpStorage::getType() const {
93 return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
94}
95
96int MtpStorage::getFileSystemType() const {
97 return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
98}
99
100int MtpStorage::getAccessCapability() const {
101 return MTP_STORAGE_READ_WRITE;
102}
103
104uint64_t MtpStorage::getMaxCapacity() {
105 if (mMaxCapacity == 0) {
106 struct statfs stat;
107 if (statfs(getPath(), &stat))
108 return -1;
109 mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
110 }
111 return mMaxCapacity;
112}
113
114uint64_t MtpStorage::getFreeSpace() {
115 struct statfs stat;
116 if (statfs(getPath(), &stat))
117 return -1;
118 return (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
119}
120
121const char* MtpStorage::getDescription() const {
122 return (const char *)mDescription;
123}
124
125int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
126 MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
127 if (handle == MTP_PARENT_ROOT) {
128 MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
129 return -1;
130 } else {
131 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
132 Node* node = i->second->findNode(handle);
133 if (node != NULL) {
134 std::string oldName = getNodePath(node);
135 std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
136 std::string newFullName = parentdir + "/" + newName;
137 MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
138 if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
139 node->rename(newName);
140 return 0;
141 } else {
142 MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
143 return -1;
144 }
145 }
146 }
147 }
148 // handle not found on this storage
149 return -1;
150}
151
152MtpObjectHandle MtpStorage::beginSendObject(const char* path,
153 MtpObjectFormat format,
154 MtpObjectHandle parent,
155 __attribute__((unused)) uint64_t size,
156 __attribute__((unused)) time_t modified) {
157 MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
158 iter it = mtpmap.find(parent);
159 if (it == mtpmap.end()) {
160 MTPE("parent node not found, returning error\n");
161 return kInvalidObjectHandle;
162 }
163 Tree* tree = it->second;
164
165 std::string pathstr(path);
166 size_t slashpos = pathstr.find_last_of('/');
167 if (slashpos == std::string::npos) {
168 MTPE("path has no slash, returning error\n");
169 return kInvalidObjectHandle;
170 }
171 std::string parentdir = pathstr.substr(0, slashpos);
172 std::string basename = pathstr.substr(slashpos + 1);
173 if (parent != 0 && parentdir != getNodePath(tree)) {
174 MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
175 return kInvalidObjectHandle;
176 }
177
178 MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
179 // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
180 bool isDir = format == MTP_FORMAT_ASSOCIATION;
181 Node* node = addNewNode(isDir, tree, basename);
182 handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
183
184 return node->Mtpid();
185}
186
187int MtpStorage::createDB() {
188 std::string mtpParent = "";
189 mtpstorageparent = getPath();
190 // root directory is special: handle 0, parent 0, and empty path
191 mtpmap[0] = new Tree(0, 0, "");
192 if (use_mutex) {
193 sendEvents = true;
194 MTPD("inotify_init\n");
195 inotify_fd = inotify_init();
196 if (inotify_fd < 0) {
197 MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
198 } else {
199 MTPD("Starting inotify thread\n");
200 inotify_thread = inotify();
201 }
202 } else {
203 MTPD("NOT starting inotify thread\n");
204 }
205 // for debugging and caching purposes, read the root dir already now
206 readDir(mtpstorageparent, mtpmap[0]);
207 // all other dirs are read on demand
208 //
209 MTPD("MtpStorage::createDB DONE\n");
210 return 0;
211}
212
213Node* MtpStorage::findNode(MtpObjectHandle handle) {
214 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
215 Node* node = i->second->findNode(handle);
216 if (node != NULL) {
217 MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
218 if (node->Mtpid() != handle)
219 {
220 MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
221 }
222 return node;
223 }
224 }
225 // Item is not on this storage device
226 MTPD("MtpStorage::findNode: no node found for handle %u on storage %u, searched %u trees\n", handle, mStorageID, mtpmap.size());
227 return NULL;
228}
229
230std::string MtpStorage::getNodePath(Node* node) {
231 std::string path;
232 MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
233 while (node)
234 {
235 path = "/" + node->getName() + path;
236 MtpObjectHandle parent = node->getMtpParentId();
237 if (parent == 0) // root
238 break;
239 node = findNode(parent);
240 }
241 path = mtpstorageparent + path;
242 MTPD("getNodePath: path %s\n", path.c_str());
243 return path;
244}
245
246MtpObjectHandleList* MtpStorage::getObjectList(__attribute__((unused)) MtpStorageID storageID, MtpObjectHandle parent) {
247 MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
248 //append object id (numerical #s) of database to int array
249 MtpObjectHandleList* list = new MtpObjectHandleList();
250 if (parent == MTP_PARENT_ROOT) {
251 MTPD("parent == MTP_PARENT_ROOT\n");
252 parent = 0;
253 }
254
255 if (mtpmap.find(parent) == mtpmap.end()) {
256 MTPE("parent handle not found, returning empty list\n");
257 return list;
258 }
259
260 Tree* tree = mtpmap[parent];
261 if (!tree->wasAlreadyRead())
262 {
263 std::string path = getNodePath(tree);
264 MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
265 readDir(path, tree);
266 }
267
268 mtpmap[parent]->getmtpids(list);
269 MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
270 return list;
271}
272
273Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
274{
275 // global counter for new object handles
276 static MtpObjectHandle mtpid = 0;
277
278 ++mtpid;
279 MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
280 MtpObjectHandle parent = tree->Mtpid();
281 MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
282 Node* node;
283 if (isDir)
284 node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
285 else
286 node = new Node(mtpid, parent, name);
287 tree->addEntry(node);
288 return node;
289}
290
291int MtpStorage::readDir(const std::string& path, Tree* tree)
292{
293 struct dirent *de;
294 int storageID = getStorageID();
295 MtpObjectHandle parent = tree->Mtpid();
296
297 DIR *d = opendir(path.c_str());
298 MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
299 if (d == NULL) {
300 MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
301 return -1;
302 }
303 // TODO: for refreshing dirs: capture old entries here
304 while ((de = readdir(d)) != NULL) {
305 // Because exfat-fuse causes issues with dirent, we will use stat
306 // for some things that dirent should be able to do
307 std::string item = path + "/" + de->d_name;
308 struct stat st;
309 if (lstat(item.c_str(), &st)) {
310 MTPE("Error running lstat on '%s'\n", item.c_str());
311 return -1;
312 }
313 // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
314 if (strcmp(de->d_name, ".") == 0)
315 continue;
316 if (strcmp(de->d_name, "..") == 0)
317 continue;
318 Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
319 node->addProperties(item, storageID);
320 //if (sendEvents)
321 // mServer->sendObjectAdded(node->Mtpid());
322 // sending events here makes simple-mtpfs very slow, and it is probably the wrong thing to do anyway
323 }
324 closedir(d);
325 // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
326 tree->setAlreadyRead(true);
327 addInotify(tree);
328 return 0;
329}
330
331int MtpStorage::addInotify(Tree* tree) {
332 if (inotify_fd < 0) {
333 MTPE("inotify_fd not set or error: %i\n", inotify_fd);
334 return -1;
335 }
336 std::string path = getNodePath(tree);
337 MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
338 int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
339 if (wd < 0) {
340 MTPE("inotify_add_watch failed: %s\n", strerror(errno));
341 return -1;
342 }
343 inotifymap[wd] = tree;
344 return 0;
345}
346
347pthread_t MtpStorage::inotify(void) {
348 pthread_t thread;
349 pthread_attr_t tattr;
350
351 if (pthread_attr_init(&tattr)) {
352 MTPE("Unable to pthread_attr_init\n");
353 return 0;
354 }
355 if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
356 MTPE("Error setting pthread_attr_setdetachstate\n");
357 return 0;
358 }
359 ThreadPtr inotifyptr = &MtpStorage::inotify_t;
360 PThreadPtr p = *(PThreadPtr*)&inotifyptr;
361 pthread_create(&thread, &tattr, p, this);
362 if (pthread_attr_destroy(&tattr)) {
363 MTPE("Failed to pthread_attr_destroy\n");
364 }
365 return thread;
366}
367
368int MtpStorage::inotify_t(void) {
369 #define EVENT_SIZE ( sizeof(struct inotify_event) )
370 #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
371 char buf[EVENT_BUF_LEN];
372 fd_set fdset;
373 struct timeval seltmout;
374 int sel_ret;
375
376 MTPD("inotify thread starting.\n");
377
378 while (inotify_thread_kill.get_value() == 0) {
379 FD_ZERO(&fdset);
380 FD_SET(inotify_fd, &fdset);
381 seltmout.tv_sec = 0;
382 seltmout.tv_usec = 25000;
383 sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout);
384 if (sel_ret == 0)
385 continue;
386 int i = 0;
387 int len = read(inotify_fd, buf, EVENT_BUF_LEN);
388
389 if (len < 0) {
390 if (errno == EINTR)
391 continue;
392 MTPE("inotify_t Can't read inotify events\n");
393 }
394
395 while (i < len && inotify_thread_kill.get_value() == 0) {
396 struct inotify_event *event = (struct inotify_event *) &buf[i];
397 if (event->len) {
398 MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
399 lockMutex(1);
400 handleInotifyEvent(event);
401 unlockMutex(1);
402 }
403 i += EVENT_SIZE + event->len;
404 }
405 }
406 MTPD("inotify_thread_kill received!\n");
407 // This cleanup is handled in the destructor.
408 /*for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
409 inotify_rm_watch(inotify_fd, i->first);
410 }
411 close(inotify_fd);*/
412 return 0;
413}
414
415void MtpStorage::handleInotifyEvent(struct inotify_event* event)
416{
417 std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
418 if (it == inotifymap.end()) {
419 MTPE("Unable to locate inotify_wd: %i\n", event->wd);
420 return;
421 }
422 Tree* tree = it->second;
423 MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
424 Node* node = tree->findEntryByName(basename(event->name));
425 if (node && node->Mtpid() == handleCurrentlySending) {
426 MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
427 return;
428 }
429 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
430 if (event->mask & IN_ISDIR) {
431 MTPD("inotify_t create is dir\n");
432 } else {
433 MTPD("inotify_t create is file\n");
434 }
435 if (node == NULL) {
436 node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
437 std::string item = getNodePath(tree) + "/" + event->name;
438 node->addProperties(item, getStorageID());
439 mServer->sendObjectAdded(node->Mtpid());
440 } else {
441 MTPD("inotify_t item already exists.\n");
442 }
443 if (event->mask & IN_ISDIR) {
444 // TODO: do we need to do anything here? probably not until someone reads from the dir...
445 }
446 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
447 if (event->mask & IN_ISDIR) {
448 MTPD("inotify_t Directory %s deleted\n", event->name);
449 } else {
450 MTPD("inotify_t File %s deleted\n", event->name);
451 }
452 if (node)
453 {
454 if (event->mask & IN_ISDIR) {
455 for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
456 if (it->second == node) {
457 inotify_rm_watch(inotify_fd, it->first);
458 MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
459 inotifymap.erase(it->first);
460 break;
461 }
462
463 }
464 }
465 MtpObjectHandle handle = node->Mtpid();
466 deleteFile(handle);
467 mServer->sendObjectRemoved(handle);
468 } else {
469 MTPD("inotify_t already removed.\n");
470 }
471 } else if (event->mask & IN_MODIFY) {
472 MTPD("inotify_t item %s modified.\n", event->name);
473 if (node != NULL) {
474 uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
475 struct stat st;
476 uint64_t new_size = 0;
477 if (lstat(getNodePath(node).c_str(), &st) == 0)
478 new_size = (uint64_t)st.st_size;
479 if (orig_size != new_size) {
480 MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
481 node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
482 mServer->sendObjectUpdated(node->Mtpid());
483 }
484 } else {
485 MTPE("inotify_t modified item not found\n");
486 }
487 } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
488 // TODO: is this always already handled by IN_DELETE for the parent dir?
489 }
490}
491
492void MtpStorage::lockMutex(int thread_type) {
493 if (!use_mutex)
494 return; // mutex is disabled
495 if (thread_type) {
496 // inotify thread
497 pthread_mutex_lock(&inMutex);
498 while (pthread_mutex_trylock(&mtpMutex)) {
499 pthread_mutex_unlock(&inMutex);
500 usleep(32000);
501 pthread_mutex_lock(&inMutex);
502 }
503 } else {
504 // main mtp thread
505 pthread_mutex_lock(&mtpMutex);
506 while (pthread_mutex_trylock(&inMutex)) {
507 pthread_mutex_unlock(&mtpMutex);
508 usleep(13000);
509 pthread_mutex_lock(&mtpMutex);
510 }
511 }
512}
513
514void MtpStorage::unlockMutex( __attribute__((unused)) int thread_type) {
515 if (!use_mutex)
516 return; // mutex is disabled
517 pthread_mutex_unlock(&inMutex);
518 pthread_mutex_unlock(&mtpMutex);
519}
520
521int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
522 Node *node;
523 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
524 node = i->second->findNode(handle);
525 if (node != NULL) {
526 const Node::mtpProperty& prop = node->getProperty(property);
527 if (prop.property != property) {
528 MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
529 return -1;
530 }
531 pe.datatype = prop.dataType;
532 pe.intvalue = prop.valueInt;
533 pe.strvalue = prop.valueStr;
534 pe.handle = handle;
535 pe.property = property;
536 return 0;
537 }
538 }
539 // handle not found on this storage
540 return -1;
541}
542
543void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, __attribute__((unused)) MtpObjectFormat format, __attribute__((unused)) bool succeeded)
544{
545 Node* node = findNode(handle);
546 if (!node)
547 return; // just ignore if this is for another storage
548
549 node->addProperties(path, mStorageID);
550 handleCurrentlySending = 0;
551 // TODO: are we supposed to send an event about an upload by the initiator?
552 if (sendEvents)
553 mServer->sendObjectAdded(node->Mtpid());
554}
555
556int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, __attribute__((unused)) int depth, MtpDataPacket& packet) {
557 MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
558 if (groupCode != 0)
559 {
560 MTPE("getObjectPropertyList: groupCode unsupported\n");
561 return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
562 }
563 // TODO: support all the special stuff, like:
564 // handle == 0 -> all objects at the root level
565 // handle == 0xffffffff -> all objects (on all storages? how could we support that?)
566 // format == 0 -> all formats, otherwise filter by ObjectFormatCode
567 // property == 0xffffffff -> all properties except those with group code 0xffffffff
568 // if property == 0 then use groupCode
569 // groupCode == 0 -> return Specification_By_Group_Unsupported
570 // depth == 0xffffffff -> all objects incl. and below handle
571
572 std::vector<PropEntry> results;
573
574 if (handle == 0xffffffff) {
575 // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
576 } else if (handle == 0) {
577 // all objects at the root level
578 Tree* root = mtpmap[0];
579 MtpObjectHandleList list;
580 root->getmtpids(&list);
581 for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
582 Node* node = root->findNode(*it);
583 if (!node) {
584 MTPE("BUG: node not found for root entry with handle %u\n", *it);
585 break;
586 }
587 queryNodeProperties(results, node, property, groupCode, mStorageID);
588 }
589 } else {
590 // single object
591 Node* node = findNode(handle);
592 if (!node) {
593 // Item is not on this storage device
594 return -1;
595 }
596 queryNodeProperties(results, node, property, groupCode, mStorageID);
597 }
598
599 MTPD("MtpStorage::getObjectPropertyList::count: %u\n", results.size());
600 packet.putUInt32(results.size());
601
602 for (size_t i = 0; i < results.size(); ++i) {
603 PropEntry& p = results[i];
604 MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
605 p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
606 p.datatype, p.intvalue);
607 packet.putUInt32(p.handle);
608 packet.putUInt16(p.property);
609 packet.putUInt16(p.datatype);
610 switch (p.datatype) {
611 case MTP_TYPE_INT8:
612 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT8\n");
613 packet.putInt8(p.intvalue);
614 break;
615 case MTP_TYPE_UINT8:
616 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT8\n");
617 packet.putUInt8(p.intvalue);
618 break;
619 case MTP_TYPE_INT16:
620 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT16\n");
621 packet.putInt16(p.intvalue);
622 break;
623 case MTP_TYPE_UINT16:
624 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT16\n");
625 packet.putUInt16(p.intvalue);
626 break;
627 case MTP_TYPE_INT32:
628 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT32\n");
629 packet.putInt32(p.intvalue);
630 break;
631 case MTP_TYPE_UINT32:
632 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT32\n");
633 packet.putUInt32(p.intvalue);
634 break;
635 case MTP_TYPE_INT64:
636 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT64\n");
637 packet.putInt64(p.intvalue);
638 break;
639 case MTP_TYPE_UINT64:
640 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT64\n");
641 packet.putUInt64(p.intvalue);
642 break;
643 case MTP_TYPE_INT128:
644 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT128\n");
645 packet.putInt128(p.intvalue);
646 break;
647 case MTP_TYPE_UINT128:
648 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT128\n");
649 packet.putUInt128(p.intvalue);
650 break;
651 case MTP_TYPE_STR:
652 MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_STR: %s\n", p.strvalue.c_str());
653 packet.putString(p.strvalue.c_str());
654 break;
655 default:
656 MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
657 break;
658 }
659 }
660 return 0;
661}
662
663int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
664 struct stat st;
665 uint64_t size = 0;
666 MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
667 Node* node = findNode(handle);
668 if (!node) {
669 // Item is not on this storage device
670 return -1;
671 }
672
673 info.mStorageID = getStorageID();
674 MTPD("info.mStorageID: %u\n", info.mStorageID);
675 info.mParent = node->getMtpParentId();
676 MTPD("mParent: %u\n", info.mParent);
677 // TODO: do we want to lstat again here, or read from the node properties?
678 if (lstat(getNodePath(node).c_str(), &st) == 0)
679 size = st.st_size;
680 MTPD("size is: %llu\n", size);
681 info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
682 info.mDateModified = st.st_mtime;
683 if (S_ISDIR(st.st_mode)) {
684 info.mFormat = MTP_FORMAT_ASSOCIATION;
685 }
686 else {
687 info.mFormat = MTP_FORMAT_UNDEFINED;
688 }
689 info.mName = strdup(node->getName().c_str());
690 MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
691 return 0;
692}
693
694int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpStringBuffer& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
695 MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
696 Node* node = findNode(handle);
697 if (!node)
698 {
699 // Item is not on this storage device
700 return -1;
701 }
702 // TODO: do we want to lstat here, or just read the info from the node?
703 struct stat st;
704 if (lstat(getNodePath(node).c_str(), &st) == 0)
705 outFileLength = st.st_size;
706 else
707 outFileLength = 0;
708 outFilePath.set(getNodePath(node).c_str());
709 MTPD("outFilePath: %s\n", (const char*) outFilePath);
710 outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
711 return 0;
712}
713
714int MtpStorage::deleteFile(MtpObjectHandle handle) {
715 MTPD("MtpStorage::deleteFile handle: %u\n", handle);
716 Node* node = findNode(handle);
717 if (!node) {
718 // Item is not on this storage device
719 return -1;
720 }
721 MtpObjectHandle parent = node->getMtpParentId();
722 Tree* tree = mtpmap[parent];
723 if (!tree) {
724 MTPE("parent tree for handle %u not found\n", parent);
725 return -1;
726 }
727 if (node->isDir()) {
728 MTPD("deleting tree from mtpmap: %u\n", handle);
729 mtpmap.erase(handle);
730 }
731
732 MTPD("deleting handle: %u\n", handle);
733 tree->deleteNode(handle);
734 MTPD("deleted\n");
735 return 0;
736}
737
738void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, __attribute__((unused)) int groupCode, MtpStorageID storageID)
739{
740 MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
741 PropEntry pe;
742 pe.handle = node->Mtpid();
743 pe.property = property;
744
745 if (property == 0xffffffff)
746 {
747 // add all properties
748 MTPD("MtpStorage::queryNodeProperties for all properties\n");
749 std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
750 for (size_t i = 0; i < mtpprop.size(); ++i) {
751 pe.property = mtpprop[i].property;
752 pe.datatype = mtpprop[i].dataType;
753 pe.intvalue = mtpprop[i].valueInt;
754 pe.strvalue = mtpprop[i].valueStr;
755 results.push_back(pe);
756 }
757 return;
758 }
759 else if (property == 0)
760 {
761 // TODO: use groupCode
762 }
763
764 // single property
765 // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
766 switch (property) {
767// case MTP_PROPERTY_OBJECT_FORMAT:
768// pe.datatype = MTP_TYPE_UINT16;
769// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
770// break;
771
772 case MTP_PROPERTY_STORAGE_ID:
773 pe.datatype = MTP_TYPE_UINT32;
774 pe.intvalue = storageID;
775 break;
776
777 case MTP_PROPERTY_PROTECTION_STATUS:
778 pe.datatype = MTP_TYPE_UINT16;
779 pe.intvalue = 0;
780 break;
781
782 case MTP_PROPERTY_OBJECT_SIZE:
783 {
784 pe.datatype = MTP_TYPE_UINT64;
785 struct stat st;
786 pe.intvalue = 0;
787 if (lstat(getNodePath(node).c_str(), &st) == 0)
788 pe.intvalue = st.st_size;
789 break;
790 }
791
792 default:
793 {
794 const Node::mtpProperty& prop = node->getProperty(property);
795 if (prop.property != property)
796 {
797 MTPD("queryNodeProperties: unknown property %x\n", property);
798 return;
799 }
800 pe.datatype = prop.dataType;
801 pe.intvalue = prop.valueInt;
802 pe.strvalue = prop.valueStr;
803 // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
804 }
805
806 }
807 results.push_back(pe);
808}
809
810