blob: ab4f8e0442c93d18c2f95d8343f05a7a6e9673ab [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>
Ethan Yonker726a0202014-12-16 20:01:38 -060039#include "tw_sys_atomics.h"
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040040
41#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
42
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040043MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
44 const char* description, uint64_t reserveSpace,
45 bool removable, uint64_t maxFileSize, MtpServer* refserver)
46 : mStorageID(id),
47 mFilePath(filePath),
48 mDescription(description),
49 mMaxCapacity(0),
50 mMaxFileSize(maxFileSize),
51 mReserveSpace(reserveSpace),
52 mRemovable(removable),
53 mServer(refserver)
54{
55 MTPI("MtpStorage id: %d path: %s\n", id, filePath);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040056 inotify_thread = 0;
Ethan Yonkercdc3ef52014-11-25 10:40:04 -060057 inotify_fd = -1;
Ethan Yonker726a0202014-12-16 20:01:38 -060058 // Threading has not started yet so we should be safe to set these directly instead of using atomics
59 inotify_thread_kill = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040060 sendEvents = false;
that9e0593e2014-10-08 00:01:24 +020061 handleCurrentlySending = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040062 use_mutex = true;
63 if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
64 MTPE("Failed to init mtpMutex\n");
65 use_mutex = false;
66 }
67 if (pthread_mutex_init(&inMutex, NULL) != 0) {
68 MTPE("Failed to init inMutex\n");
Ethan Yonker726a0202014-12-16 20:01:38 -060069 pthread_mutex_destroy(&mtpMutex);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040070 use_mutex = false;
71 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040072}
73
74MtpStorage::~MtpStorage() {
75 if (inotify_thread) {
Ethan Yonker726a0202014-12-16 20:01:38 -060076 __tw_atomic_cmpxchg(0, 1, &inotify_thread_kill);
77 //inotify_thread_kill = 1;
78 MTPD("joining inotify_thread after sending the kill notification.\n");
79 pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here
80 inotify_thread = 0;
81 MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n");
that9e0593e2014-10-08 00:01:24 +020082 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040083 inotify_rm_watch(inotify_fd, i->first);
84 }
85 close(inotify_fd);
Ethan Yonker726a0202014-12-16 20:01:38 -060086 inotifymap.clear();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040087 }
Ethan Yonker726a0202014-12-16 20:01:38 -060088 // Deleting the root tree causes a cascade in btree.cpp that ends up
89 // deleting all of the trees and nodes.
90 delete mtpmap[0];
91 mtpmap.clear();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040092 if (use_mutex) {
93 use_mutex = false;
Ethan Yonker726a0202014-12-16 20:01:38 -060094 MTPD("~MtpStorage destroying mutexes\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040095 pthread_mutex_destroy(&mtpMutex);
96 pthread_mutex_destroy(&inMutex);
97 }
98}
99
100int MtpStorage::getType() const {
101 return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
102}
103
104int MtpStorage::getFileSystemType() const {
105 return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
106}
107
108int MtpStorage::getAccessCapability() const {
109 return MTP_STORAGE_READ_WRITE;
110}
111
112uint64_t MtpStorage::getMaxCapacity() {
113 if (mMaxCapacity == 0) {
114 struct statfs stat;
115 if (statfs(getPath(), &stat))
116 return -1;
117 mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
118 }
119 return mMaxCapacity;
120}
121
122uint64_t MtpStorage::getFreeSpace() {
123 struct statfs stat;
124 if (statfs(getPath(), &stat))
125 return -1;
126 uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
127 return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
128}
129
130const char* MtpStorage::getDescription() const {
131 return (const char *)mDescription;
132}
133
134int MtpStorage::createDB() {
135 std::string mtpParent = "";
136 mtpstorageparent = getPath();
that9e0593e2014-10-08 00:01:24 +0200137 // root directory is special: handle 0, parent 0, and empty path
138 mtpmap[0] = new Tree(0, 0, "");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400139 MTPD("MtpStorage::createDB DONE\n");
140 if (use_mutex) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400141 sendEvents = true;
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600142 MTPD("inotify_init\n");
143 inotify_fd = inotify_init();
144 if (inotify_fd < 0) {
145 MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
146 } else {
147 MTPD("Starting inotify thread\n");
148 inotify_thread = inotify();
149 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400150 } else {
151 MTPD("NOT starting inotify thread\n");
152 }
that9e0593e2014-10-08 00:01:24 +0200153 // for debugging and caching purposes, read the root dir already now
154 readDir(mtpstorageparent, mtpmap[0]);
155 // all other dirs are read on demand
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400156 return 0;
157}
158
159MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
that9e0593e2014-10-08 00:01:24 +0200160 MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400161 //append object id (numerical #s) of database to int array
162 MtpObjectHandleList* list = new MtpObjectHandleList();
163 if (parent == MTP_PARENT_ROOT) {
164 MTPD("parent == MTP_PARENT_ROOT\n");
that9e0593e2014-10-08 00:01:24 +0200165 parent = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400166 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400167
that9e0593e2014-10-08 00:01:24 +0200168 if (mtpmap.find(parent) == mtpmap.end()) {
169 MTPE("parent handle not found, returning empty list\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400170 return list;
171 }
172
that9e0593e2014-10-08 00:01:24 +0200173 Tree* tree = mtpmap[parent];
174 if (!tree->wasAlreadyRead())
175 {
176 std::string path = getNodePath(tree);
177 MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
178 readDir(path, tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400179 }
that9e0593e2014-10-08 00:01:24 +0200180
181 mtpmap[parent]->getmtpids(list);
182 MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400183 return list;
184}
185
186int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
187 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500188 uint64_t size = 0;
that9e0593e2014-10-08 00:01:24 +0200189 MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
190 Node* node = findNode(handle);
191 if (!node) {
192 // Item is not on this storage device
193 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400194 }
that9e0593e2014-10-08 00:01:24 +0200195
196 info.mStorageID = getStorageID();
197 MTPD("info.mStorageID: %u\n", info.mStorageID);
198 info.mParent = node->getMtpParentId();
199 MTPD("mParent: %u\n", info.mParent);
200 // TODO: do we want to lstat again here, or read from the node properties?
201 if (lstat(getNodePath(node).c_str(), &st) == 0)
202 size = st.st_size;
203 MTPD("size is: %llu\n", size);
204 info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
205 info.mDateModified = st.st_mtime;
206 if (S_ISDIR(st.st_mode)) {
207 info.mFormat = MTP_FORMAT_ASSOCIATION;
208 }
209 else {
210 info.mFormat = MTP_FORMAT_UNDEFINED;
211 }
212 info.mName = strdup(node->getName().c_str());
213 MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
214 return 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400215}
216
217MtpObjectHandle MtpStorage::beginSendObject(const char* path,
218 MtpObjectFormat format,
219 MtpObjectHandle parent,
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400220 uint64_t size,
221 time_t modified) {
that9e0593e2014-10-08 00:01:24 +0200222 MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
223 iter it = mtpmap.find(parent);
224 if (it == mtpmap.end()) {
225 MTPE("parent node not found, returning error\n");
226 return kInvalidObjectHandle;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400227 }
that9e0593e2014-10-08 00:01:24 +0200228 Tree* tree = it->second;
229
230 std::string pathstr(path);
231 size_t slashpos = pathstr.find_last_of('/');
232 if (slashpos == std::string::npos) {
233 MTPE("path has no slash, returning error\n");
234 return kInvalidObjectHandle;
235 }
236 std::string parentdir = pathstr.substr(0, slashpos);
237 std::string basename = pathstr.substr(slashpos + 1);
238 if (parent != 0 && parentdir != getNodePath(tree)) {
239 MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
240 return kInvalidObjectHandle;
241 }
242
243 MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
244 // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
245 bool isDir = format == MTP_FORMAT_ASSOCIATION;
246 Node* node = addNewNode(isDir, tree, basename);
247 handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
248
249 return node->Mtpid();
250}
251
252void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded)
253{
254 Node* node = findNode(handle);
255 if (!node)
256 return; // just ignore if this is for another storage
257
258 node->addProperties(path, mStorageID);
259 handleCurrentlySending = 0;
260 // TODO: are we supposed to send an event about an upload by the initiator?
261 if (sendEvents)
262 mServer->sendObjectAdded(node->Mtpid());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400263}
264
265int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
that9e0593e2014-10-08 00:01:24 +0200266 MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
267 Node* node = findNode(handle);
268 if (!node)
269 {
270 // Item is not on this storage device
271 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400272 }
that9e0593e2014-10-08 00:01:24 +0200273 // TODO: do we want to lstat here, or just read the info from the node?
274 struct stat st;
275 if (lstat(getNodePath(node).c_str(), &st) == 0)
276 outFileLength = st.st_size;
277 else
278 outFileLength = 0;
279 outFilePath = getNodePath(node).c_str();
280 MTPD("outFilePath: %s\n", outFilePath.string());
281 outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400282 return 0;
283}
284
that9e0593e2014-10-08 00:01:24 +0200285int MtpStorage::readDir(const std::string& path, Tree* tree)
286{
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400287 struct dirent *de;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400288 int storageID = getStorageID();
that9e0593e2014-10-08 00:01:24 +0200289 MtpObjectHandle parent = tree->Mtpid();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400290
that9e0593e2014-10-08 00:01:24 +0200291 DIR *d = opendir(path.c_str());
292 MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400293 if (d == NULL) {
that9e0593e2014-10-08 00:01:24 +0200294 MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
295 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400296 }
that9e0593e2014-10-08 00:01:24 +0200297 // TODO: for refreshing dirs: capture old entries here
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400298 while ((de = readdir(d)) != NULL) {
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500299 // Because exfat-fuse causes issues with dirent, we will use stat
300 // for some things that dirent should be able to do
that9e0593e2014-10-08 00:01:24 +0200301 std::string item = path + "/" + de->d_name;
302 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500303 if (lstat(item.c_str(), &st)) {
304 MTPE("Error running lstat on '%s'\n", item.c_str());
305 return -1;
306 }
that9e0593e2014-10-08 00:01:24 +0200307 // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
308 if (strcmp(de->d_name, ".") == 0)
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400309 continue;
that9e0593e2014-10-08 00:01:24 +0200310 if (strcmp(de->d_name, "..") == 0)
311 continue;
312 Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
313 node->addProperties(item, storageID);
314 //if (sendEvents)
315 // mServer->sendObjectAdded(node->Mtpid());
316 // 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 -0400317 }
that9e0593e2014-10-08 00:01:24 +0200318 closedir(d);
319 // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
320 tree->setAlreadyRead(true);
321 addInotify(tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400322 return 0;
323}
324
that9e0593e2014-10-08 00:01:24 +0200325int MtpStorage::deleteFile(MtpObjectHandle handle) {
326 MTPD("MtpStorage::deleteFile handle: %u\n", handle);
327 Node* node = findNode(handle);
328 if (!node) {
329 // Item is not on this storage device
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400330 return -1;
331 }
that9e0593e2014-10-08 00:01:24 +0200332 MtpObjectHandle parent = node->getMtpParentId();
333 Tree* tree = mtpmap[parent];
334 if (!tree) {
335 MTPE("parent tree for handle %u not found\n", parent);
336 return -1;
337 }
338 if (node->isDir()) {
339 MTPD("deleting tree from mtpmap: %u\n", handle);
340 mtpmap.erase(handle);
341 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400342
that9e0593e2014-10-08 00:01:24 +0200343 MTPD("deleting handle: %u\n", handle);
344 tree->deleteNode(handle);
345 MTPD("deleted\n");
346 return 0;
347}
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400348
that9e0593e2014-10-08 00:01:24 +0200349void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID)
350{
351 MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
352 PropEntry pe;
353 pe.handle = node->Mtpid();
354 pe.property = property;
355
356 if (property == 0xffffffff)
357 {
358 // add all properties
359 MTPD("MtpStorage::queryNodeProperties for all properties\n");
360 std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
361 for (size_t i = 0; i < mtpprop.size(); ++i) {
362 pe.property = mtpprop[i].property;
363 pe.datatype = mtpprop[i].dataType;
364 pe.intvalue = mtpprop[i].valueInt;
365 pe.strvalue = mtpprop[i].valueStr;
366 results.push_back(pe);
367 }
368 return;
369 }
370 else if (property == 0)
371 {
372 // TODO: use groupCode
373 }
374
375 // single property
376 // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
377 switch (property) {
378// case MTP_PROPERTY_OBJECT_FORMAT:
379// pe.datatype = MTP_TYPE_UINT16;
380// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
381// break;
382
383 case MTP_PROPERTY_STORAGE_ID:
384 pe.datatype = MTP_TYPE_UINT32;
385 pe.intvalue = storageID;
386 break;
387
388 case MTP_PROPERTY_PROTECTION_STATUS:
389 pe.datatype = MTP_TYPE_UINT16;
390 pe.intvalue = 0;
391 break;
392
393 case MTP_PROPERTY_OBJECT_SIZE:
394 {
395 pe.datatype = MTP_TYPE_UINT64;
396 struct stat st;
397 pe.intvalue = 0;
398 if (lstat(getNodePath(node).c_str(), &st) == 0)
399 pe.intvalue = st.st_size;
400 break;
401 }
402
403 default:
404 {
405 const Node::mtpProperty& prop = node->getProperty(property);
406 if (prop.property != property)
407 {
408 MTPD("queryNodeProperties: unknown property %x\n", property);
409 return;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400410 }
that9e0593e2014-10-08 00:01:24 +0200411 pe.datatype = prop.dataType;
412 pe.intvalue = prop.valueInt;
413 pe.strvalue = prop.valueStr;
414 // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
415 }
416
417 }
418 results.push_back(pe);
419}
420
421int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
422 MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
423 if (groupCode != 0)
424 {
425 MTPE("getObjectPropertyList: groupCode unsupported\n");
426 return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
427 }
428 // TODO: support all the special stuff, like:
429 // handle == 0 -> all objects at the root level
430 // handle == 0xffffffff -> all objects (on all storages? how could we support that?)
431 // format == 0 -> all formats, otherwise filter by ObjectFormatCode
432 // property == 0xffffffff -> all properties except those with group code 0xffffffff
433 // if property == 0 then use groupCode
434 // groupCode == 0 -> return Specification_By_Group_Unsupported
435 // depth == 0xffffffff -> all objects incl. and below handle
436
437 std::vector<PropEntry> results;
438
439 if (handle == 0xffffffff) {
440 // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
441 } else if (handle == 0) {
442 // all objects at the root level
443 Tree* root = mtpmap[0];
444 MtpObjectHandleList list;
445 root->getmtpids(&list);
446 for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
447 Node* node = root->findNode(*it);
448 if (!node) {
449 MTPE("BUG: node not found for root entry with handle %u\n", *it);
450 break;
451 }
452 queryNodeProperties(results, node, property, groupCode, mStorageID);
453 }
454 } else {
455 // single object
456 Node* node = findNode(handle);
457 if (!node) {
458 // Item is not on this storage device
459 return -1;
460 }
461 queryNodeProperties(results, node, property, groupCode, mStorageID);
462 }
463
464 MTPD("count: %u\n", results.size());
465 packet.putUInt32(results.size());
466
467 for (size_t i = 0; i < results.size(); ++i) {
468 PropEntry& p = results[i];
469 MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
470 p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
471 p.datatype, p.intvalue);
472 packet.putUInt32(p.handle);
473 packet.putUInt16(p.property);
474 packet.putUInt16(p.datatype);
475 switch (p.datatype) {
476 case MTP_TYPE_INT8:
477 MTPD("MTP_TYPE_INT8\n");
478 packet.putInt8(p.intvalue);
479 break;
480 case MTP_TYPE_UINT8:
481 MTPD("MTP_TYPE_UINT8\n");
482 packet.putUInt8(p.intvalue);
483 break;
484 case MTP_TYPE_INT16:
485 MTPD("MTP_TYPE_INT16\n");
486 packet.putInt16(p.intvalue);
487 break;
488 case MTP_TYPE_UINT16:
489 MTPD("MTP_TYPE_UINT16\n");
490 packet.putUInt16(p.intvalue);
491 break;
492 case MTP_TYPE_INT32:
493 MTPD("MTP_TYPE_INT32\n");
494 packet.putInt32(p.intvalue);
495 break;
496 case MTP_TYPE_UINT32:
497 MTPD("MTP_TYPE_UINT32\n");
498 packet.putUInt32(p.intvalue);
499 break;
500 case MTP_TYPE_INT64:
501 MTPD("MTP_TYPE_INT64\n");
502 packet.putInt64(p.intvalue);
503 break;
504 case MTP_TYPE_UINT64:
505 MTPD("MTP_TYPE_UINT64\n");
506 packet.putUInt64(p.intvalue);
507 break;
508 case MTP_TYPE_INT128:
509 MTPD("MTP_TYPE_INT128\n");
510 packet.putInt128(p.intvalue);
511 break;
512 case MTP_TYPE_UINT128:
513 MTPD("MTP_TYPE_UINT128\n");
514 packet.putUInt128(p.intvalue);
515 break;
516 case MTP_TYPE_STR:
517 MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str());
518 packet.putString(p.strvalue.c_str());
519 break;
520 default:
521 MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
522 break;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400523 }
524 }
525 return 0;
526}
527
528int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
that9e0593e2014-10-08 00:01:24 +0200529 MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400530 if (handle == MTP_PARENT_ROOT) {
531 MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
532 return -1;
533 } else {
534 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200535 Node* node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400536 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200537 std::string oldName = getNodePath(node);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400538 std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
539 std::string newFullName = parentdir + "/" + newName;
540 MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
541 if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
that9e0593e2014-10-08 00:01:24 +0200542 node->rename(newName);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400543 return 0;
544 } else {
that9e0593e2014-10-08 00:01:24 +0200545 MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400546 return -1;
547 }
548 }
549 }
550 }
Ethan Yonker6f49e112014-09-03 21:42:49 -0500551 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400552 return -1;
553}
554
that9e0593e2014-10-08 00:01:24 +0200555int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400556 Node *node;
557 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200558 node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400559 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200560 const Node::mtpProperty& prop = node->getProperty(property);
561 if (prop.property != property) {
562 MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
563 return -1;
564 }
565 pe.datatype = prop.dataType;
566 pe.intvalue = prop.valueInt;
567 pe.strvalue = prop.valueStr;
568 pe.handle = handle;
569 pe.property = property;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400570 return 0;
571 }
572 }
Ethan Yonker5e083dc2014-09-03 14:46:41 -0500573 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400574 return -1;
575}
that9e0593e2014-10-08 00:01:24 +0200576
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400577pthread_t MtpStorage::inotify(void) {
578 pthread_t thread;
Ethan Yonker726a0202014-12-16 20:01:38 -0600579 pthread_attr_t tattr;
580
581 if (pthread_attr_init(&tattr)) {
582 MTPE("Unable to pthread_attr_init\n");
583 return 0;
584 }
585 if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
586 MTPE("Error setting pthread_attr_setdetachstate\n");
587 return 0;
588 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400589 ThreadPtr inotifyptr = &MtpStorage::inotify_t;
590 PThreadPtr p = *(PThreadPtr*)&inotifyptr;
Ethan Yonker726a0202014-12-16 20:01:38 -0600591 pthread_create(&thread, &tattr, p, this);
592 if (pthread_attr_destroy(&tattr)) {
593 MTPE("Failed to pthread_attr_destroy\n");
594 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400595 return thread;
596}
597
that9e0593e2014-10-08 00:01:24 +0200598int MtpStorage::addInotify(Tree* tree) {
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600599 if (inotify_fd < 0) {
600 MTPE("inotify_fd not set or error: %i\n", inotify_fd);
601 return -1;
602 }
that9e0593e2014-10-08 00:01:24 +0200603 std::string path = getNodePath(tree);
604 MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
605 int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
606 if (wd < 0) {
607 MTPE("inotify_add_watch failed: %s\n", strerror(errno));
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400608 return -1;
609 }
that9e0593e2014-10-08 00:01:24 +0200610 inotifymap[wd] = tree;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400611 return 0;
612}
613
that9e0593e2014-10-08 00:01:24 +0200614void MtpStorage::handleInotifyEvent(struct inotify_event* event)
615{
616 std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
617 if (it == inotifymap.end()) {
618 MTPE("Unable to locate inotify_wd: %i\n", event->wd);
619 return;
620 }
621 Tree* tree = it->second;
622 MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
623 Node* node = tree->findEntryByName(basename(event->name));
624 if (node && node->Mtpid() == handleCurrentlySending) {
625 MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
626 return;
627 }
628 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
629 if (event->mask & IN_ISDIR) {
630 MTPD("inotify_t create is dir\n");
631 } else {
632 MTPD("inotify_t create is file\n");
633 }
634 if (node == NULL) {
635 node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
636 std::string item = getNodePath(tree) + "/" + event->name;
637 node->addProperties(item, getStorageID());
638 mServer->sendObjectAdded(node->Mtpid());
639 } else {
640 MTPD("inotify_t item already exists.\n");
641 }
642 if (event->mask & IN_ISDIR) {
643 // TODO: do we need to do anything here? probably not until someone reads from the dir...
644 }
645 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
646 if (event->mask & IN_ISDIR) {
647 MTPD("inotify_t Directory %s deleted\n", event->name);
648 } else {
649 MTPD("inotify_t File %s deleted\n", event->name);
650 }
651 if (node)
652 {
653 if (event->mask & IN_ISDIR) {
654 for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
655 if (it->second == node) {
656 inotify_rm_watch(inotify_fd, it->first);
657 MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
658 inotifymap.erase(it->first);
659 break;
660 }
661
662 }
663 }
664 MtpObjectHandle handle = node->Mtpid();
665 deleteFile(handle);
666 mServer->sendObjectRemoved(handle);
667 } else {
668 MTPD("inotify_t already removed.\n");
669 }
670 } else if (event->mask & IN_MODIFY) {
671 MTPD("inotify_t item %s modified.\n", event->name);
672 if (node != NULL) {
673 uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
674 struct stat st;
675 uint64_t new_size = 0;
676 if (lstat(getNodePath(node).c_str(), &st) == 0)
677 new_size = (uint64_t)st.st_size;
678 if (orig_size != new_size) {
679 MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
680 node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
681 mServer->sendObjectUpdated(node->Mtpid());
682 }
683 } else {
684 MTPE("inotify_t modified item not found\n");
685 }
686 } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
687 // TODO: is this always already handled by IN_DELETE for the parent dir?
688 }
689}
690
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400691int MtpStorage::inotify_t(void) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400692 #define EVENT_SIZE ( sizeof(struct inotify_event) )
693 #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
694 char buf[EVENT_BUF_LEN];
Ethan Yonker726a0202014-12-16 20:01:38 -0600695 fd_set fdset;
696 struct timeval seltmout;
697 int sel_ret;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400698
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600699 MTPD("inotify thread starting.\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400700
Ethan Yonker726a0202014-12-16 20:01:38 -0600701 while (__tw_atomic_cmpxchg(0, inotify_thread_kill, &inotify_thread_kill) == 0) {
702 FD_ZERO(&fdset);
703 FD_SET(inotify_fd, &fdset);
704 seltmout.tv_sec = 0;
705 seltmout.tv_usec = 25000;
706 sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout);
707 if (sel_ret == 0)
708 continue;
that9e0593e2014-10-08 00:01:24 +0200709 int i = 0;
710 int len = read(inotify_fd, buf, EVENT_BUF_LEN);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400711
712 if (len < 0) {
that9e0593e2014-10-08 00:01:24 +0200713 if (errno == EINTR)
714 continue;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400715 MTPE("inotify_t Can't read inotify events\n");
716 }
717
Ethan Yonker726a0202014-12-16 20:01:38 -0600718 while (i < len && __tw_atomic_cmpxchg(0, inotify_thread_kill, &inotify_thread_kill) == 0) {
that9e0593e2014-10-08 00:01:24 +0200719 struct inotify_event *event = (struct inotify_event *) &buf[i];
720 if (event->len) {
721 MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
722 lockMutex(1);
723 handleInotifyEvent(event);
724 unlockMutex(1);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400725 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400726 i += EVENT_SIZE + event->len;
727 }
728 }
Ethan Yonker726a0202014-12-16 20:01:38 -0600729 MTPD("inotify_thread_kill received!\n");
730 // This cleanup is handled in the destructor.
731 /*for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400732 inotify_rm_watch(inotify_fd, i->first);
733 }
Ethan Yonker726a0202014-12-16 20:01:38 -0600734 close(inotify_fd);*/
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400735 return 0;
736}
737
that9e0593e2014-10-08 00:01:24 +0200738Node* MtpStorage::findNodeByPath(const std::string& path) {
739 MTPD("findNodeByPath: %s\n", path.c_str());
740 std::string match = path.substr(0, mtpstorageparent.size());
741 if (match != mtpstorageparent) {
742 // not on this device
743 MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str());
744 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400745 }
that9e0593e2014-10-08 00:01:24 +0200746
747 // TODO: fix and test this
748 std::string p = path.substr(mtpstorageparent.size()+1); // cut off "/" after storage root too
749 Tree* tree = mtpmap[0]; // start at storage root
750
751 Node* node = NULL;
752 while (!p.empty()) {
753 size_t slashpos = p.find('/');
754 std::string e;
755 if (slashpos != std::string::npos) {
756 e = p;
757 p.clear();
758 } else {
759 e = p.substr(0, slashpos);
760 p = p.substr(slashpos + 1);
761 }
762 MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str());
763 node = tree->findEntryByName(e);
764 if (!node) {
765 MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str());
766 return NULL;
767 }
768 if (node->isDir())
769 tree = static_cast<Tree*>(node);
770 else if (!p.empty()) {
771 MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node);
772 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400773 }
774 }
that9e0593e2014-10-08 00:01:24 +0200775 MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str());
776 return node;
777}
778
779Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
780{
781 // global counter for new object handles
782 static MtpObjectHandle mtpid = 0;
783
784 ++mtpid;
785 MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
786 MtpObjectHandle parent = tree->Mtpid();
787 MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
788 Node* node;
789 if (isDir)
790 node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
791 else
792 node = new Node(mtpid, parent, name);
793 tree->addEntry(node);
794 return node;
795}
796
797Node* MtpStorage::findNode(MtpObjectHandle handle) {
798 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
799 Node* node = i->second->findNode(handle);
800 if (node != NULL) {
801 MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
802 if (node->Mtpid() != handle)
803 {
804 MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
805 }
806 return node;
807 }
808 }
809 // Item is not on this storage device
that5823d482014-10-13 00:15:05 +0200810 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 +0200811 return NULL;
812}
813
814std::string MtpStorage::getNodePath(Node* node) {
815 std::string path;
816 MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
817 while (node)
818 {
819 path = "/" + node->getName() + path;
820 MtpObjectHandle parent = node->getMtpParentId();
821 if (parent == 0) // root
822 break;
823 node = findNode(parent);
824 }
825 path = mtpstorageparent + path;
826 MTPD("getNodePath: path %s\n", path.c_str());
827 return path;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400828}
829
830void MtpStorage::lockMutex(int thread_type) {
831 if (!use_mutex)
832 return; // mutex is disabled
833 if (thread_type) {
834 // inotify thread
835 pthread_mutex_lock(&inMutex);
836 while (pthread_mutex_trylock(&mtpMutex)) {
837 pthread_mutex_unlock(&inMutex);
838 usleep(32000);
839 pthread_mutex_lock(&inMutex);
840 }
841 } else {
842 // main mtp thread
843 pthread_mutex_lock(&mtpMutex);
844 while (pthread_mutex_trylock(&inMutex)) {
845 pthread_mutex_unlock(&mtpMutex);
846 usleep(13000);
847 pthread_mutex_lock(&mtpMutex);
848 }
849 }
850}
851
852void MtpStorage::unlockMutex(int thread_type) {
853 if (!use_mutex)
854 return; // mutex is disabled
855 pthread_mutex_unlock(&inMutex);
856 pthread_mutex_unlock(&mtpMutex);
857}