blob: b22158a123bc0bff3f486747e830d25add3e3cec [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 bigbiffaf32bb92018-12-18 18:39:53 -050039#include "../../tw_atomic.hpp"
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
Ethan Yonker1b67fff2015-01-27 08:50:48 -060059 inotify_thread_kill.set_value(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 Yonker1b67fff2015-01-27 08:50:48 -060076 inotify_thread_kill.set_value(1);
Ethan Yonker726a0202014-12-16 20:01:38 -060077 MTPD("joining inotify_thread after sending the kill notification.\n");
78 pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here
79 inotify_thread = 0;
80 MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n");
that9e0593e2014-10-08 00:01:24 +020081 for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040082 inotify_rm_watch(inotify_fd, i->first);
83 }
84 close(inotify_fd);
Ethan Yonker726a0202014-12-16 20:01:38 -060085 inotifymap.clear();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040086 }
Ethan Yonker726a0202014-12-16 20:01:38 -060087 // Deleting the root tree causes a cascade in btree.cpp that ends up
88 // deleting all of the trees and nodes.
89 delete mtpmap[0];
90 mtpmap.clear();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040091 if (use_mutex) {
92 use_mutex = false;
Ethan Yonker726a0202014-12-16 20:01:38 -060093 MTPD("~MtpStorage destroying mutexes\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040094 pthread_mutex_destroy(&mtpMutex);
95 pthread_mutex_destroy(&inMutex);
96 }
97}
98
99int MtpStorage::getType() const {
100 return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
101}
102
103int MtpStorage::getFileSystemType() const {
104 return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
105}
106
107int MtpStorage::getAccessCapability() const {
108 return MTP_STORAGE_READ_WRITE;
109}
110
111uint64_t MtpStorage::getMaxCapacity() {
112 if (mMaxCapacity == 0) {
bigbiff bigbiffaf32bb92018-12-18 18:39:53 -0500113 struct statfs stat;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400114 if (statfs(getPath(), &stat))
115 return -1;
116 mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
117 }
118 return mMaxCapacity;
119}
120
121uint64_t MtpStorage::getFreeSpace() {
bigbiff bigbiffaf32bb92018-12-18 18:39:53 -0500122 struct statfs stat;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400123 if (statfs(getPath(), &stat))
124 return -1;
125 uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
126 return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
127}
128
129const char* MtpStorage::getDescription() const {
130 return (const char *)mDescription;
131}
132
133int MtpStorage::createDB() {
134 std::string mtpParent = "";
135 mtpstorageparent = getPath();
that9e0593e2014-10-08 00:01:24 +0200136 // root directory is special: handle 0, parent 0, and empty path
137 mtpmap[0] = new Tree(0, 0, "");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400138 MTPD("MtpStorage::createDB DONE\n");
139 if (use_mutex) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400140 sendEvents = true;
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600141 MTPD("inotify_init\n");
142 inotify_fd = inotify_init();
143 if (inotify_fd < 0) {
144 MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
145 } else {
146 MTPD("Starting inotify thread\n");
147 inotify_thread = inotify();
148 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400149 } else {
150 MTPD("NOT starting inotify thread\n");
151 }
that9e0593e2014-10-08 00:01:24 +0200152 // for debugging and caching purposes, read the root dir already now
153 readDir(mtpstorageparent, mtpmap[0]);
154 // all other dirs are read on demand
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400155 return 0;
156}
157
158MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
that9e0593e2014-10-08 00:01:24 +0200159 MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
bigbiff bigbiffaf32bb92018-12-18 18:39:53 -0500160 //append object id (numerical #s) of database to int array
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400161 MtpObjectHandleList* list = new MtpObjectHandleList();
162 if (parent == MTP_PARENT_ROOT) {
163 MTPD("parent == MTP_PARENT_ROOT\n");
that9e0593e2014-10-08 00:01:24 +0200164 parent = 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400165 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400166
that9e0593e2014-10-08 00:01:24 +0200167 if (mtpmap.find(parent) == mtpmap.end()) {
168 MTPE("parent handle not found, returning empty list\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400169 return list;
170 }
171
that9e0593e2014-10-08 00:01:24 +0200172 Tree* tree = mtpmap[parent];
173 if (!tree->wasAlreadyRead())
174 {
175 std::string path = getNodePath(tree);
176 MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
177 readDir(path, tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400178 }
that9e0593e2014-10-08 00:01:24 +0200179
180 mtpmap[parent]->getmtpids(list);
181 MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400182 return list;
183}
184
185int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
186 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500187 uint64_t size = 0;
that9e0593e2014-10-08 00:01:24 +0200188 MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
189 Node* node = findNode(handle);
190 if (!node) {
191 // Item is not on this storage device
192 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400193 }
that9e0593e2014-10-08 00:01:24 +0200194
195 info.mStorageID = getStorageID();
196 MTPD("info.mStorageID: %u\n", info.mStorageID);
197 info.mParent = node->getMtpParentId();
198 MTPD("mParent: %u\n", info.mParent);
199 // TODO: do we want to lstat again here, or read from the node properties?
200 if (lstat(getNodePath(node).c_str(), &st) == 0)
201 size = st.st_size;
202 MTPD("size is: %llu\n", size);
203 info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
204 info.mDateModified = st.st_mtime;
205 if (S_ISDIR(st.st_mode)) {
206 info.mFormat = MTP_FORMAT_ASSOCIATION;
207 }
208 else {
209 info.mFormat = MTP_FORMAT_UNDEFINED;
210 }
211 info.mName = strdup(node->getName().c_str());
212 MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
213 return 0;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400214}
215
216MtpObjectHandle MtpStorage::beginSendObject(const char* path,
217 MtpObjectFormat format,
218 MtpObjectHandle parent,
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400219 uint64_t size,
220 time_t modified) {
that9e0593e2014-10-08 00:01:24 +0200221 MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
222 iter it = mtpmap.find(parent);
223 if (it == mtpmap.end()) {
224 MTPE("parent node not found, returning error\n");
225 return kInvalidObjectHandle;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400226 }
that9e0593e2014-10-08 00:01:24 +0200227 Tree* tree = it->second;
228
229 std::string pathstr(path);
230 size_t slashpos = pathstr.find_last_of('/');
231 if (slashpos == std::string::npos) {
232 MTPE("path has no slash, returning error\n");
233 return kInvalidObjectHandle;
234 }
235 std::string parentdir = pathstr.substr(0, slashpos);
236 std::string basename = pathstr.substr(slashpos + 1);
237 if (parent != 0 && parentdir != getNodePath(tree)) {
238 MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
239 return kInvalidObjectHandle;
240 }
241
242 MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
243 // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
244 bool isDir = format == MTP_FORMAT_ASSOCIATION;
245 Node* node = addNewNode(isDir, tree, basename);
246 handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
247
248 return node->Mtpid();
249}
250
251void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded)
252{
253 Node* node = findNode(handle);
254 if (!node)
255 return; // just ignore if this is for another storage
256
257 node->addProperties(path, mStorageID);
258 handleCurrentlySending = 0;
259 // TODO: are we supposed to send an event about an upload by the initiator?
260 if (sendEvents)
261 mServer->sendObjectAdded(node->Mtpid());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400262}
263
264int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
that9e0593e2014-10-08 00:01:24 +0200265 MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
266 Node* node = findNode(handle);
267 if (!node)
268 {
269 // Item is not on this storage device
270 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400271 }
that9e0593e2014-10-08 00:01:24 +0200272 // TODO: do we want to lstat here, or just read the info from the node?
273 struct stat st;
274 if (lstat(getNodePath(node).c_str(), &st) == 0)
275 outFileLength = st.st_size;
276 else
277 outFileLength = 0;
278 outFilePath = getNodePath(node).c_str();
279 MTPD("outFilePath: %s\n", outFilePath.string());
280 outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400281 return 0;
282}
283
that9e0593e2014-10-08 00:01:24 +0200284int MtpStorage::readDir(const std::string& path, Tree* tree)
285{
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400286 struct dirent *de;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400287 int storageID = getStorageID();
that9e0593e2014-10-08 00:01:24 +0200288 MtpObjectHandle parent = tree->Mtpid();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400289
that9e0593e2014-10-08 00:01:24 +0200290 DIR *d = opendir(path.c_str());
291 MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400292 if (d == NULL) {
that9e0593e2014-10-08 00:01:24 +0200293 MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
294 return -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400295 }
that9e0593e2014-10-08 00:01:24 +0200296 // TODO: for refreshing dirs: capture old entries here
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400297 while ((de = readdir(d)) != NULL) {
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500298 // Because exfat-fuse causes issues with dirent, we will use stat
299 // for some things that dirent should be able to do
that9e0593e2014-10-08 00:01:24 +0200300 std::string item = path + "/" + de->d_name;
301 struct stat st;
Ethan Yonker241a3ce2014-09-04 12:59:27 -0500302 if (lstat(item.c_str(), &st)) {
303 MTPE("Error running lstat on '%s'\n", item.c_str());
304 return -1;
305 }
that9e0593e2014-10-08 00:01:24 +0200306 // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
307 if (strcmp(de->d_name, ".") == 0)
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400308 continue;
that9e0593e2014-10-08 00:01:24 +0200309 if (strcmp(de->d_name, "..") == 0)
310 continue;
311 Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
312 node->addProperties(item, storageID);
313 //if (sendEvents)
314 // mServer->sendObjectAdded(node->Mtpid());
315 // 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 -0400316 }
that9e0593e2014-10-08 00:01:24 +0200317 closedir(d);
318 // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
319 tree->setAlreadyRead(true);
320 addInotify(tree);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400321 return 0;
322}
323
that9e0593e2014-10-08 00:01:24 +0200324int MtpStorage::deleteFile(MtpObjectHandle handle) {
325 MTPD("MtpStorage::deleteFile handle: %u\n", handle);
326 Node* node = findNode(handle);
327 if (!node) {
328 // Item is not on this storage device
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400329 return -1;
330 }
that9e0593e2014-10-08 00:01:24 +0200331 MtpObjectHandle parent = node->getMtpParentId();
332 Tree* tree = mtpmap[parent];
333 if (!tree) {
334 MTPE("parent tree for handle %u not found\n", parent);
335 return -1;
336 }
337 if (node->isDir()) {
338 MTPD("deleting tree from mtpmap: %u\n", handle);
339 mtpmap.erase(handle);
340 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400341
that9e0593e2014-10-08 00:01:24 +0200342 MTPD("deleting handle: %u\n", handle);
343 tree->deleteNode(handle);
344 MTPD("deleted\n");
345 return 0;
346}
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400347
that9e0593e2014-10-08 00:01:24 +0200348void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID)
349{
350 MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
351 PropEntry pe;
352 pe.handle = node->Mtpid();
353 pe.property = property;
354
355 if (property == 0xffffffff)
356 {
357 // add all properties
358 MTPD("MtpStorage::queryNodeProperties for all properties\n");
359 std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
360 for (size_t i = 0; i < mtpprop.size(); ++i) {
361 pe.property = mtpprop[i].property;
362 pe.datatype = mtpprop[i].dataType;
363 pe.intvalue = mtpprop[i].valueInt;
364 pe.strvalue = mtpprop[i].valueStr;
365 results.push_back(pe);
366 }
367 return;
368 }
369 else if (property == 0)
370 {
371 // TODO: use groupCode
372 }
373
374 // single property
375 // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
376 switch (property) {
377// case MTP_PROPERTY_OBJECT_FORMAT:
378// pe.datatype = MTP_TYPE_UINT16;
379// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
380// break;
381
382 case MTP_PROPERTY_STORAGE_ID:
383 pe.datatype = MTP_TYPE_UINT32;
384 pe.intvalue = storageID;
385 break;
386
387 case MTP_PROPERTY_PROTECTION_STATUS:
388 pe.datatype = MTP_TYPE_UINT16;
389 pe.intvalue = 0;
390 break;
391
392 case MTP_PROPERTY_OBJECT_SIZE:
393 {
394 pe.datatype = MTP_TYPE_UINT64;
395 struct stat st;
396 pe.intvalue = 0;
397 if (lstat(getNodePath(node).c_str(), &st) == 0)
398 pe.intvalue = st.st_size;
399 break;
400 }
401
402 default:
403 {
404 const Node::mtpProperty& prop = node->getProperty(property);
405 if (prop.property != property)
406 {
407 MTPD("queryNodeProperties: unknown property %x\n", property);
408 return;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400409 }
that9e0593e2014-10-08 00:01:24 +0200410 pe.datatype = prop.dataType;
411 pe.intvalue = prop.valueInt;
412 pe.strvalue = prop.valueStr;
413 // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
414 }
415
416 }
417 results.push_back(pe);
418}
419
420int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
421 MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
422 if (groupCode != 0)
423 {
424 MTPE("getObjectPropertyList: groupCode unsupported\n");
425 return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
426 }
427 // TODO: support all the special stuff, like:
428 // handle == 0 -> all objects at the root level
429 // handle == 0xffffffff -> all objects (on all storages? how could we support that?)
430 // format == 0 -> all formats, otherwise filter by ObjectFormatCode
431 // property == 0xffffffff -> all properties except those with group code 0xffffffff
432 // if property == 0 then use groupCode
bigbiff bigbiffaf32bb92018-12-18 18:39:53 -0500433 // groupCode == 0 -> return Specification_By_Group_Unsupported
that9e0593e2014-10-08 00:01:24 +0200434 // depth == 0xffffffff -> all objects incl. and below handle
435
436 std::vector<PropEntry> results;
437
438 if (handle == 0xffffffff) {
439 // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
440 } else if (handle == 0) {
441 // all objects at the root level
442 Tree* root = mtpmap[0];
443 MtpObjectHandleList list;
444 root->getmtpids(&list);
445 for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
446 Node* node = root->findNode(*it);
447 if (!node) {
448 MTPE("BUG: node not found for root entry with handle %u\n", *it);
449 break;
450 }
451 queryNodeProperties(results, node, property, groupCode, mStorageID);
452 }
453 } else {
454 // single object
455 Node* node = findNode(handle);
456 if (!node) {
457 // Item is not on this storage device
458 return -1;
459 }
460 queryNodeProperties(results, node, property, groupCode, mStorageID);
461 }
462
463 MTPD("count: %u\n", results.size());
464 packet.putUInt32(results.size());
465
466 for (size_t i = 0; i < results.size(); ++i) {
467 PropEntry& p = results[i];
468 MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
469 p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
470 p.datatype, p.intvalue);
471 packet.putUInt32(p.handle);
472 packet.putUInt16(p.property);
473 packet.putUInt16(p.datatype);
474 switch (p.datatype) {
475 case MTP_TYPE_INT8:
476 MTPD("MTP_TYPE_INT8\n");
477 packet.putInt8(p.intvalue);
478 break;
479 case MTP_TYPE_UINT8:
480 MTPD("MTP_TYPE_UINT8\n");
481 packet.putUInt8(p.intvalue);
482 break;
483 case MTP_TYPE_INT16:
484 MTPD("MTP_TYPE_INT16\n");
485 packet.putInt16(p.intvalue);
486 break;
487 case MTP_TYPE_UINT16:
488 MTPD("MTP_TYPE_UINT16\n");
489 packet.putUInt16(p.intvalue);
490 break;
491 case MTP_TYPE_INT32:
492 MTPD("MTP_TYPE_INT32\n");
493 packet.putInt32(p.intvalue);
494 break;
495 case MTP_TYPE_UINT32:
496 MTPD("MTP_TYPE_UINT32\n");
497 packet.putUInt32(p.intvalue);
498 break;
499 case MTP_TYPE_INT64:
500 MTPD("MTP_TYPE_INT64\n");
501 packet.putInt64(p.intvalue);
502 break;
503 case MTP_TYPE_UINT64:
504 MTPD("MTP_TYPE_UINT64\n");
505 packet.putUInt64(p.intvalue);
506 break;
507 case MTP_TYPE_INT128:
508 MTPD("MTP_TYPE_INT128\n");
509 packet.putInt128(p.intvalue);
510 break;
511 case MTP_TYPE_UINT128:
512 MTPD("MTP_TYPE_UINT128\n");
513 packet.putUInt128(p.intvalue);
514 break;
515 case MTP_TYPE_STR:
516 MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str());
517 packet.putString(p.strvalue.c_str());
518 break;
519 default:
520 MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
521 break;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400522 }
523 }
524 return 0;
525}
526
527int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
that9e0593e2014-10-08 00:01:24 +0200528 MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400529 if (handle == MTP_PARENT_ROOT) {
530 MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
531 return -1;
532 } else {
533 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200534 Node* node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400535 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200536 std::string oldName = getNodePath(node);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400537 std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
538 std::string newFullName = parentdir + "/" + newName;
539 MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
540 if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
that9e0593e2014-10-08 00:01:24 +0200541 node->rename(newName);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400542 return 0;
543 } else {
that9e0593e2014-10-08 00:01:24 +0200544 MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400545 return -1;
546 }
547 }
548 }
549 }
Ethan Yonker6f49e112014-09-03 21:42:49 -0500550 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400551 return -1;
552}
553
that9e0593e2014-10-08 00:01:24 +0200554int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400555 Node *node;
556 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
that9e0593e2014-10-08 00:01:24 +0200557 node = i->second->findNode(handle);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400558 if (node != NULL) {
that9e0593e2014-10-08 00:01:24 +0200559 const Node::mtpProperty& prop = node->getProperty(property);
560 if (prop.property != property) {
561 MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
562 return -1;
563 }
564 pe.datatype = prop.dataType;
565 pe.intvalue = prop.valueInt;
566 pe.strvalue = prop.valueStr;
567 pe.handle = handle;
568 pe.property = property;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400569 return 0;
570 }
571 }
Ethan Yonker5e083dc2014-09-03 14:46:41 -0500572 // handle not found on this storage
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400573 return -1;
574}
that9e0593e2014-10-08 00:01:24 +0200575
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400576pthread_t MtpStorage::inotify(void) {
577 pthread_t thread;
Ethan Yonker726a0202014-12-16 20:01:38 -0600578 pthread_attr_t tattr;
579
580 if (pthread_attr_init(&tattr)) {
581 MTPE("Unable to pthread_attr_init\n");
582 return 0;
583 }
584 if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
585 MTPE("Error setting pthread_attr_setdetachstate\n");
586 return 0;
587 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400588 ThreadPtr inotifyptr = &MtpStorage::inotify_t;
589 PThreadPtr p = *(PThreadPtr*)&inotifyptr;
Ethan Yonker726a0202014-12-16 20:01:38 -0600590 pthread_create(&thread, &tattr, p, this);
591 if (pthread_attr_destroy(&tattr)) {
592 MTPE("Failed to pthread_attr_destroy\n");
593 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400594 return thread;
595}
596
that9e0593e2014-10-08 00:01:24 +0200597int MtpStorage::addInotify(Tree* tree) {
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600598 if (inotify_fd < 0) {
599 MTPE("inotify_fd not set or error: %i\n", inotify_fd);
600 return -1;
601 }
that9e0593e2014-10-08 00:01:24 +0200602 std::string path = getNodePath(tree);
603 MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
604 int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
605 if (wd < 0) {
606 MTPE("inotify_add_watch failed: %s\n", strerror(errno));
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400607 return -1;
608 }
that9e0593e2014-10-08 00:01:24 +0200609 inotifymap[wd] = tree;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400610 return 0;
611}
612
that9e0593e2014-10-08 00:01:24 +0200613void MtpStorage::handleInotifyEvent(struct inotify_event* event)
614{
615 std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
616 if (it == inotifymap.end()) {
617 MTPE("Unable to locate inotify_wd: %i\n", event->wd);
618 return;
619 }
620 Tree* tree = it->second;
621 MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
622 Node* node = tree->findEntryByName(basename(event->name));
623 if (node && node->Mtpid() == handleCurrentlySending) {
624 MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
625 return;
626 }
627 if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
628 if (event->mask & IN_ISDIR) {
629 MTPD("inotify_t create is dir\n");
630 } else {
631 MTPD("inotify_t create is file\n");
632 }
633 if (node == NULL) {
634 node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
635 std::string item = getNodePath(tree) + "/" + event->name;
636 node->addProperties(item, getStorageID());
637 mServer->sendObjectAdded(node->Mtpid());
638 } else {
639 MTPD("inotify_t item already exists.\n");
640 }
641 if (event->mask & IN_ISDIR) {
642 // TODO: do we need to do anything here? probably not until someone reads from the dir...
643 }
644 } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
645 if (event->mask & IN_ISDIR) {
646 MTPD("inotify_t Directory %s deleted\n", event->name);
647 } else {
648 MTPD("inotify_t File %s deleted\n", event->name);
649 }
650 if (node)
651 {
652 if (event->mask & IN_ISDIR) {
653 for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
654 if (it->second == node) {
655 inotify_rm_watch(inotify_fd, it->first);
656 MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
657 inotifymap.erase(it->first);
658 break;
659 }
660
661 }
662 }
663 MtpObjectHandle handle = node->Mtpid();
664 deleteFile(handle);
665 mServer->sendObjectRemoved(handle);
666 } else {
667 MTPD("inotify_t already removed.\n");
668 }
669 } else if (event->mask & IN_MODIFY) {
670 MTPD("inotify_t item %s modified.\n", event->name);
671 if (node != NULL) {
672 uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
673 struct stat st;
674 uint64_t new_size = 0;
675 if (lstat(getNodePath(node).c_str(), &st) == 0)
676 new_size = (uint64_t)st.st_size;
677 if (orig_size != new_size) {
678 MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
679 node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
680 mServer->sendObjectUpdated(node->Mtpid());
681 }
682 } else {
683 MTPE("inotify_t modified item not found\n");
684 }
685 } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
686 // TODO: is this always already handled by IN_DELETE for the parent dir?
687 }
688}
689
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400690int MtpStorage::inotify_t(void) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400691 #define EVENT_SIZE ( sizeof(struct inotify_event) )
692 #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
693 char buf[EVENT_BUF_LEN];
Ethan Yonker726a0202014-12-16 20:01:38 -0600694 fd_set fdset;
695 struct timeval seltmout;
696 int sel_ret;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400697
Ethan Yonkercdc3ef52014-11-25 10:40:04 -0600698 MTPD("inotify thread starting.\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400699
Ethan Yonker1b67fff2015-01-27 08:50:48 -0600700 while (inotify_thread_kill.get_value() == 0) {
Ethan Yonker726a0202014-12-16 20:01:38 -0600701 FD_ZERO(&fdset);
702 FD_SET(inotify_fd, &fdset);
703 seltmout.tv_sec = 0;
704 seltmout.tv_usec = 25000;
705 sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout);
706 if (sel_ret == 0)
707 continue;
that9e0593e2014-10-08 00:01:24 +0200708 int i = 0;
709 int len = read(inotify_fd, buf, EVENT_BUF_LEN);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400710
711 if (len < 0) {
that9e0593e2014-10-08 00:01:24 +0200712 if (errno == EINTR)
713 continue;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400714 MTPE("inotify_t Can't read inotify events\n");
715 }
716
Ethan Yonker13f7ae92015-02-09 09:33:36 -0600717 while (i < len && inotify_thread_kill.get_value() == 0) {
that9e0593e2014-10-08 00:01:24 +0200718 struct inotify_event *event = (struct inotify_event *) &buf[i];
719 if (event->len) {
720 MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
721 lockMutex(1);
722 handleInotifyEvent(event);
723 unlockMutex(1);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400724 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400725 i += EVENT_SIZE + event->len;
726 }
727 }
Ethan Yonker726a0202014-12-16 20:01:38 -0600728 MTPD("inotify_thread_kill received!\n");
729 // This cleanup is handled in the destructor.
730 /*for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400731 inotify_rm_watch(inotify_fd, i->first);
732 }
Ethan Yonker726a0202014-12-16 20:01:38 -0600733 close(inotify_fd);*/
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400734 return 0;
735}
736
that9e0593e2014-10-08 00:01:24 +0200737Node* MtpStorage::findNodeByPath(const std::string& path) {
738 MTPD("findNodeByPath: %s\n", path.c_str());
739 std::string match = path.substr(0, mtpstorageparent.size());
740 if (match != mtpstorageparent) {
741 // not on this device
742 MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str());
743 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400744 }
that9e0593e2014-10-08 00:01:24 +0200745
746 // TODO: fix and test this
747 std::string p = path.substr(mtpstorageparent.size()+1); // cut off "/" after storage root too
748 Tree* tree = mtpmap[0]; // start at storage root
749
750 Node* node = NULL;
751 while (!p.empty()) {
752 size_t slashpos = p.find('/');
753 std::string e;
754 if (slashpos != std::string::npos) {
755 e = p;
756 p.clear();
757 } else {
758 e = p.substr(0, slashpos);
759 p = p.substr(slashpos + 1);
760 }
761 MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str());
762 node = tree->findEntryByName(e);
763 if (!node) {
764 MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str());
765 return NULL;
766 }
767 if (node->isDir())
768 tree = static_cast<Tree*>(node);
769 else if (!p.empty()) {
770 MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node);
771 return NULL;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400772 }
773 }
that9e0593e2014-10-08 00:01:24 +0200774 MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str());
775 return node;
776}
777
778Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
779{
780 // global counter for new object handles
781 static MtpObjectHandle mtpid = 0;
782
783 ++mtpid;
784 MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
785 MtpObjectHandle parent = tree->Mtpid();
786 MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
787 Node* node;
788 if (isDir)
789 node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
790 else
791 node = new Node(mtpid, parent, name);
792 tree->addEntry(node);
793 return node;
794}
795
796Node* MtpStorage::findNode(MtpObjectHandle handle) {
797 for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
798 Node* node = i->second->findNode(handle);
799 if (node != NULL) {
800 MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
801 if (node->Mtpid() != handle)
802 {
803 MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
804 }
805 return node;
806 }
807 }
808 // Item is not on this storage device
that5823d482014-10-13 00:15:05 +0200809 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 +0200810 return NULL;
811}
812
813std::string MtpStorage::getNodePath(Node* node) {
814 std::string path;
815 MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
816 while (node)
817 {
818 path = "/" + node->getName() + path;
819 MtpObjectHandle parent = node->getMtpParentId();
820 if (parent == 0) // root
821 break;
822 node = findNode(parent);
823 }
824 path = mtpstorageparent + path;
825 MTPD("getNodePath: path %s\n", path.c_str());
826 return path;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400827}
828
829void MtpStorage::lockMutex(int thread_type) {
830 if (!use_mutex)
831 return; // mutex is disabled
832 if (thread_type) {
833 // inotify thread
834 pthread_mutex_lock(&inMutex);
835 while (pthread_mutex_trylock(&mtpMutex)) {
836 pthread_mutex_unlock(&inMutex);
837 usleep(32000);
838 pthread_mutex_lock(&inMutex);
839 }
840 } else {
841 // main mtp thread
842 pthread_mutex_lock(&mtpMutex);
843 while (pthread_mutex_trylock(&inMutex)) {
844 pthread_mutex_unlock(&mtpMutex);
845 usleep(13000);
846 pthread_mutex_lock(&mtpMutex);
847 }
848 }
849}
850
851void MtpStorage::unlockMutex(int thread_type) {
852 if (!use_mutex)
853 return; // mutex is disabled
854 pthread_mutex_unlock(&inMutex);
855 pthread_mutex_unlock(&mtpMutex);
856}