blob: 9cd67324f040af613fde6fc13ef39a5cc4a61dac [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 <stdio.h>
20#include <stdlib.h>
21#include <sys/types.h>
22#include <sys/ioctl.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <errno.h>
26#include <sys/stat.h>
27#include <dirent.h>
28#include "../twcommon.h"
Ethan Yonker4b94cfd2014-12-11 10:00:45 -060029#include "../set_metadata.h"
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -040030#include <cutils/properties.h>
31
32#include "MtpTypes.h"
33#include "MtpDebug.h"
34#include "MtpDatabase.h"
35#include "MtpObjectInfo.h"
36#include "MtpProperty.h"
37#include "MtpServer.h"
38#include "MtpStorage.h"
39#include "MtpStringBuffer.h"
40
41#include <linux/usb/f_mtp.h>
42
43static const MtpOperationCode kSupportedOperationCodes[] = {
44 MTP_OPERATION_GET_DEVICE_INFO,
45 MTP_OPERATION_OPEN_SESSION,
46 MTP_OPERATION_CLOSE_SESSION,
47 MTP_OPERATION_GET_STORAGE_IDS,
48 MTP_OPERATION_GET_STORAGE_INFO,
49 MTP_OPERATION_GET_NUM_OBJECTS,
50 MTP_OPERATION_GET_OBJECT_HANDLES,
51 MTP_OPERATION_GET_OBJECT_INFO,
52 MTP_OPERATION_GET_OBJECT,
53 MTP_OPERATION_GET_THUMB,
54 MTP_OPERATION_DELETE_OBJECT,
55 MTP_OPERATION_SEND_OBJECT_INFO,
56 MTP_OPERATION_SEND_OBJECT,
57// MTP_OPERATION_INITIATE_CAPTURE,
58// MTP_OPERATION_FORMAT_STORE,
59// MTP_OPERATION_RESET_DEVICE,
60// MTP_OPERATION_SELF_TEST,
61// MTP_OPERATION_SET_OBJECT_PROTECTION,
62// MTP_OPERATION_POWER_DOWN,
63 MTP_OPERATION_GET_DEVICE_PROP_DESC,
64 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
65 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
66 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
67// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
68// MTP_OPERATION_MOVE_OBJECT,
69// MTP_OPERATION_COPY_OBJECT,
70 MTP_OPERATION_GET_PARTIAL_OBJECT,
71// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
72 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
73 MTP_OPERATION_GET_OBJECT_PROP_DESC,
74 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
75 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
76 MTP_OPERATION_GET_OBJECT_PROP_LIST,
77// MTP_OPERATION_SET_OBJECT_PROP_LIST,
78// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
79// MTP_OPERATION_SEND_OBJECT_PROP_LIST,
80 MTP_OPERATION_GET_OBJECT_REFERENCES,
81 MTP_OPERATION_SET_OBJECT_REFERENCES,
82// MTP_OPERATION_SKIP,
83 // Android extension for direct file IO
84 MTP_OPERATION_GET_PARTIAL_OBJECT_64,
85 MTP_OPERATION_SEND_PARTIAL_OBJECT,
86 MTP_OPERATION_TRUNCATE_OBJECT,
87 MTP_OPERATION_BEGIN_EDIT_OBJECT,
88 MTP_OPERATION_END_EDIT_OBJECT,
89};
90
91static const MtpEventCode kSupportedEventCodes[] = {
92 MTP_EVENT_OBJECT_ADDED,
93 MTP_EVENT_OBJECT_REMOVED,
94 MTP_EVENT_STORE_ADDED,
95 MTP_EVENT_STORE_REMOVED,
96 MTP_EVENT_OBJECT_PROP_CHANGED,
97};
98
Ethan Yonker1b039202015-01-30 10:08:48 -060099MtpServer::MtpServer(MtpDatabase* database, bool ptp,
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400100 int fileGroup, int filePerm, int directoryPerm)
Ethan Yonker1b039202015-01-30 10:08:48 -0600101 : mDatabase(database),
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400102 mPtp(ptp),
103 mFileGroup(fileGroup),
104 mFilePermission(filePerm),
105 mDirectoryPermission(directoryPerm),
106 mSessionID(0),
107 mSessionOpen(false),
108 mSendObjectHandle(kInvalidObjectHandle),
109 mSendObjectFormat(0),
110 mSendObjectFileSize(0)
111{
Ethan Yonker1b039202015-01-30 10:08:48 -0600112 mFD = -1;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400113}
114
115MtpServer::~MtpServer() {
116}
117
118void MtpServer::addStorage(MtpStorage* storage) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400119 android::Mutex::Autolock autoLock(mMutex);
Ethan Yonker726a0202014-12-16 20:01:38 -0600120 MTPD("addStorage(): storage: %x\n", storage);
121 if (getStorage(storage->getStorageID()) != NULL) {
122 MTPE("MtpServer::addStorage Storage for storage ID %i already exists.\n", storage->getStorageID());
123 return;
124 }
125 mDatabase->createDB(storage, storage->getStorageID());
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400126 mStorages.push(storage);
127 sendStoreAdded(storage->getStorageID());
128}
129
130void MtpServer::removeStorage(MtpStorage* storage) {
131 android::Mutex::Autolock autoLock(mMutex);
132
133 for (size_t i = 0; i < mStorages.size(); i++) {
134 if (mStorages[i] == storage) {
Ethan Yonker726a0202014-12-16 20:01:38 -0600135 MTPD("MtpServer::removeStorage calling sendStoreRemoved\n");
136 // First lock the mutex so that the inotify thread and main
137 // thread do not do anything while we remove the storage
138 // item, and to make sure we don't remove the item while an
139 // operation is in progress
140 mDatabase->lockMutex();
141 // Grab the storage ID before we delete the item from the
142 // database
143 MtpStorageID storageID = storage->getStorageID();
144 // Remove the item from the mStorages from the vector. At
145 // this point the main thread will no longer be able to find
146 // this storage item anymore.
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400147 mStorages.removeAt(i);
Ethan Yonker726a0202014-12-16 20:01:38 -0600148 // Destroy the storage item, free up all the memory, kill
149 // the inotify thread.
150 mDatabase->destroyDB(storageID);
151 // Tell the host OS that the storage item is gone.
152 sendStoreRemoved(storageID);
153 // Unlock any remaining mutexes on other storage devices.
154 // If no storage devices exist anymore this will do nothing.
155 mDatabase->unlockMutex();
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400156 break;
157 }
158 }
Ethan Yonker726a0202014-12-16 20:01:38 -0600159 MTPD("MtpServer::removeStorage DONE\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400160}
161
162MtpStorage* MtpServer::getStorage(MtpStorageID id) {
163 MTPD("getStorage\n");
164 if (id == 0) {
165 MTPD("mStorages\n");
166 return mStorages[0];
167 }
168 for (size_t i = 0; i < mStorages.size(); i++) {
169 MtpStorage* storage = mStorages[i];
170 MTPD("id: %d\n", id);
171 MTPD("storage: %d\n", storage->getStorageID());
172 if (storage->getStorageID() == id) {
173 return storage;
174 }
175 }
176 return NULL;
177}
178
179bool MtpServer::hasStorage(MtpStorageID id) {
180 MTPD("in hasStorage\n");
181 if (id == 0 || id == 0xFFFFFFFF)
182 return mStorages.size() > 0;
183 return (getStorage(id) != NULL);
184}
185
Ethan Yonker1b039202015-01-30 10:08:48 -0600186void MtpServer::run(int fd) {
187 if (fd < 0)
188 return;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400189
Ethan Yonker1b039202015-01-30 10:08:48 -0600190 mFD = fd;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400191 MTPI("MtpServer::run fd: %d\n", fd);
192
193 while (1) {
194 MTPD("About to read device...\n");
195 int ret = mRequest.read(fd);
196 if (ret < 0) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400197 if (errno == ECANCELED) {
198 // return to top of loop and wait for next command
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600199 MTPD("request read returned %d ECANCELED, starting over\n", ret);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400200 continue;
201 }
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600202 MTPE("request read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400203 break;
204 }
205 MtpOperationCode operation = mRequest.getOperationCode();
206 MtpTransactionID transaction = mRequest.getTransactionID();
207
208 MTPD("operation: %s", MtpDebug::getOperationCodeName(operation));
209 mRequest.dump();
210
211 // FIXME need to generalize this
212 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
213 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
214 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
215 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
216 if (dataIn) {
217 int ret = mData.read(fd);
218 if (ret < 0) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400219 if (errno == ECANCELED) {
220 // return to top of loop and wait for next command
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600221 MTPD("data read returned %d ECANCELED, starting over\n", ret);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400222 continue;
223 }
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600224 MTPD("data read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400225 break;
226 }
227 MTPD("received data:");
228 mData.dump();
229 } else {
230 mData.reset();
231 }
232
233 if (handleRequest()) {
234 if (!dataIn && mData.hasData()) {
235 mData.setOperationCode(operation);
236 mData.setTransactionID(transaction);
237 MTPD("sending data:");
238 mData.dump();
239 ret = mData.write(fd);
240 if (ret < 0) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400241 if (errno == ECANCELED) {
242 // return to top of loop and wait for next command
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600243 MTPD("data write returned %d ECANCELED, starting over\n", ret);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400244 continue;
245 }
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600246 MTPE("data write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400247 break;
248 }
249 }
250
251 mResponse.setTransactionID(transaction);
252 MTPD("sending response %04X\n", mResponse.getResponseCode());
253 ret = mResponse.write(fd);
254 MTPD("ret: %d\n", ret);
255 mResponse.dump();
256 if (ret < 0) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400257 if (errno == ECANCELED) {
258 // return to top of loop and wait for next command
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600259 MTPD("response write returned %d ECANCELED, starting over\n", ret);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400260 continue;
261 }
Ethan Yonkeree8b3142014-12-29 08:39:19 -0600262 MTPE("response write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400263 break;
264 }
265 } else {
266 MTPD("skipping response\n");
267 }
268 }
269
270 // commit any open edits
271 int count = mObjectEditList.size();
272 for (int i = 0; i < count; i++) {
273 ObjectEdit* edit = mObjectEditList[i];
274 commitEdit(edit);
275 delete edit;
276 }
277 mObjectEditList.clear();
278
279 if (mSessionOpen)
Ethan Yonker1b039202015-01-30 10:08:48 -0600280 mDatabase->sessionEnded(); // This doesn't actually do anything but was carry over from AOSP
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400281 close(fd);
282 mFD = -1;
283}
284
285void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
286 MTPD("sendObjectAdded %d\n", handle);
287 sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
288}
289
290void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
291 MTPD("sendObjectRemoved %d\n", handle);
292 sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
293}
294
295void MtpServer::sendObjectUpdated(MtpObjectHandle handle) {
296 MTPD("sendObjectUpdated %d\n", handle);
297 sendEvent(MTP_EVENT_OBJECT_PROP_CHANGED, handle);
298}
299
300void MtpServer::sendStoreAdded(MtpStorageID id) {
301 MTPD("sendStoreAdded %08X\n", id);
302 sendEvent(MTP_EVENT_STORE_ADDED, id);
303}
304
305void MtpServer::sendStoreRemoved(MtpStorageID id) {
306 MTPD("sendStoreRemoved %08X\n", id);
307 sendEvent(MTP_EVENT_STORE_REMOVED, id);
Ethan Yonker726a0202014-12-16 20:01:38 -0600308 MTPD("MtpServer::sendStoreRemoved done\n");
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -0400309}
310
311void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
312 MTPD("MtpServer::sendEvent sending event code: %x\n", code);
313 if (mSessionOpen) {
314 mEvent.setEventCode(code);
315 mEvent.setTransactionID(mRequest.getTransactionID());
316 mEvent.setParameter(1, param1);
317 int ret = mEvent.write(mFD);
318 MTPD("mEvent.write returned %d\n", ret);
319 }
320}
321
322void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
323 uint64_t size, MtpObjectFormat format, int fd) {
324 ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
325 mObjectEditList.add(edit);
326}
327
328MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
329 int count = mObjectEditList.size();
330 for (int i = 0; i < count; i++) {
331 ObjectEdit* edit = mObjectEditList[i];
332 if (edit->mHandle == handle) return edit;
333 }
334 return NULL;
335}
336
337void MtpServer::removeEditObject(MtpObjectHandle handle) {
338 int count = mObjectEditList.size();
339 for (int i = 0; i < count; i++) {
340 ObjectEdit* edit = mObjectEditList[i];
341 if (edit->mHandle == handle) {
342 delete edit;
343 mObjectEditList.removeAt(i);
344 return;
345 }
346 }
347 MTPE("ObjectEdit not found in removeEditObject");
348}
349
350void MtpServer::commitEdit(ObjectEdit* edit) {
351 mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
352}
353
354
355bool MtpServer::handleRequest() {
356 android::Mutex::Autolock autoLock(mMutex);
357
358 MtpOperationCode operation = mRequest.getOperationCode();
359 MtpResponseCode response;
360
361 mResponse.reset();
362
363 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
364 // FIXME - need to delete mSendObjectHandle from the database
365 MTPE("expected SendObject after SendObjectInfo");
366 mSendObjectHandle = kInvalidObjectHandle;
367 }
368
369 switch (operation) {
370 case MTP_OPERATION_GET_DEVICE_INFO:
371 MTPD("doGetDeviceInfo()\n");
372 response = doGetDeviceInfo();
373 break;
374 case MTP_OPERATION_OPEN_SESSION:
375 MTPD("doOpenSesion()\n");
376 response = doOpenSession();
377 break;
378 case MTP_OPERATION_CLOSE_SESSION:
379 MTPD("doCloseSession()\n");
380 response = doCloseSession();
381 break;
382 case MTP_OPERATION_GET_STORAGE_IDS:
383 MTPD("doGetStorageIDs()\n");
384 response = doGetStorageIDs();
385 break;
386 case MTP_OPERATION_GET_STORAGE_INFO:
387 MTPD("about to call doGetStorageInfo()\n");
388 response = doGetStorageInfo();
389 break;
390 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
391 MTPD("about to call doGetObjectPropsSupported()\n");
392 response = doGetObjectPropsSupported();
393 break;
394 case MTP_OPERATION_GET_OBJECT_HANDLES:
395 MTPD("about to call doGetObjectHandles()\n");
396 response = doGetObjectHandles();
397 break;
398 case MTP_OPERATION_GET_NUM_OBJECTS:
399 MTPD("about to call doGetNumbObjects()\n");
400 response = doGetNumObjects();
401 break;
402 case MTP_OPERATION_GET_OBJECT_REFERENCES:
403 MTPD("about to call doGetObjectReferences()\n");
404 response = doGetObjectReferences();
405 break;
406 case MTP_OPERATION_SET_OBJECT_REFERENCES:
407 MTPD("about to call doSetObjectReferences()\n");
408 response = doSetObjectReferences();
409 break;
410 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
411 MTPD("about to call doGetObjectPropValue()\n");
412 response = doGetObjectPropValue();
413 break;
414 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
415 MTPD("about to call doSetObjectPropValue()\n");
416 response = doSetObjectPropValue();
417 break;
418 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
419 MTPD("about to call doGetDevicPropValue()\n");
420 response = doGetDevicePropValue();
421 break;
422 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
423 MTPD("about to call doSetDevicePropVaue()\n");
424 response = doSetDevicePropValue();
425 break;
426 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
427 MTPD("about to call doResetDevicePropValue()\n");
428 response = doResetDevicePropValue();
429 break;
430 case MTP_OPERATION_GET_OBJECT_PROP_LIST:
431 MTPD("calling doGetObjectPropList()\n");
432 response = doGetObjectPropList();
433 break;
434 case MTP_OPERATION_GET_OBJECT_INFO:
435 MTPD("calling doGetObjectInfo()\n");
436 response = doGetObjectInfo();
437 break;
438 case MTP_OPERATION_GET_OBJECT:
439 MTPD("about to call doGetObject()\n");
440 response = doGetObject();
441 break;
442 case MTP_OPERATION_GET_THUMB:
443 response = doGetThumb();
444 break;
445 case MTP_OPERATION_GET_PARTIAL_OBJECT:
446 case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
447 response = doGetPartialObject(operation);
448 break;
449 case MTP_OPERATION_SEND_OBJECT_INFO:
450 MTPD("about to call doSendObjectInfo()\n");
451 response = doSendObjectInfo();
452 break;
453 case MTP_OPERATION_SEND_OBJECT:
454 MTPD("about to call doSendObject()\n");
455 response = doSendObject();
456 break;
457 case MTP_OPERATION_DELETE_OBJECT:
458 response = doDeleteObject();
459 break;
460 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
461 MTPD("about to call doGetObjectPropDesc()\n");
462 response = doGetObjectPropDesc();
463 break;
464 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
465 MTPD("about to call doGetDevicePropDesc()\n");
466 response = doGetDevicePropDesc();
467 break;
468 case MTP_OPERATION_SEND_PARTIAL_OBJECT:
469 response = doSendPartialObject();
470 break;
471 case MTP_OPERATION_TRUNCATE_OBJECT:
472 response = doTruncateObject();
473 break;
474 case MTP_OPERATION_BEGIN_EDIT_OBJECT:
475 response = doBeginEditObject();
476 break;
477 case MTP_OPERATION_END_EDIT_OBJECT:
478 response = doEndEditObject();
479 break;
480 default:
481 MTPE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
482 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
483 break;
484 }
485
486 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
487 return false;
488 mResponse.setResponseCode(response);
489 return true;
490}
491
492MtpResponseCode MtpServer::doGetDeviceInfo() {
493 MtpStringBuffer string;
494 char prop_value[PROPERTY_VALUE_MAX];
495
496 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
497 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
498 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
499
500 // fill in device info
501 mData.putUInt16(MTP_STANDARD_VERSION);
502 if (mPtp) {
503 MTPD("doGetDeviceInfo putting 0\n");
504 mData.putUInt32(0);
505 } else {
506 // MTP Vendor Extension ID
507 MTPD("doGetDeviceInfo putting 6\n");
508 mData.putUInt32(6);
509 }
510 mData.putUInt16(MTP_STANDARD_VERSION);
511 if (mPtp) {
512 // no extensions
513 MTPD("doGetDeviceInfo no extensions\n");
514 string.set("");
515 } else {
516 // MTP extensions
517 MTPD("doGetDeviceInfo microsoft.com: 1.0; android.com: 1.0;\n");
518 string.set("microsoft.com: 1.0; android.com: 1.0;");
519 }
520 mData.putString(string); // MTP Extensions
521 mData.putUInt16(0); //Functional Mode
522 MTPD("doGetDeviceInfo opcodes, %i\n", sizeof(kSupportedOperationCodes) / sizeof(uint16_t));
523 MTPD("doGetDeviceInfo eventcodes, %i\n", sizeof(kSupportedEventCodes) / sizeof(uint16_t));
524 mData.putAUInt16(kSupportedOperationCodes,
525 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
526 mData.putAUInt16(kSupportedEventCodes,
527 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
528 mData.putAUInt16(deviceProperties); // Device Properties Supported
529 mData.putAUInt16(captureFormats); // Capture Formats
530 mData.putAUInt16(playbackFormats); // Playback Formats
531
532 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
533 MTPD("prop: %s\n", prop_value);
534 string.set(prop_value);
535 mData.putString(string); // Manufacturer
536
537 property_get("ro.product.model", prop_value, "MTP Device");
538 string.set(prop_value);
539 mData.putString(string); // Model
540 string.set("1.0");
541 mData.putString(string); // Device Version
542
543 property_get("ro.serialno", prop_value, "????????");
544 MTPD("sn: %s\n", prop_value);
545 string.set(prop_value);
546 mData.putString(string); // Serial Number
547
548 delete playbackFormats;
549 delete captureFormats;
550 delete deviceProperties;
551
552 return MTP_RESPONSE_OK;
553}
554
555MtpResponseCode MtpServer::doOpenSession() {
556 if (mSessionOpen) {
557 mResponse.setParameter(1, mSessionID);
558 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
559 }
560 mSessionID = mRequest.getParameter(1);
561 mSessionOpen = true;
562
563 mDatabase->sessionStarted();
564
565 return MTP_RESPONSE_OK;
566}
567
568MtpResponseCode MtpServer::doCloseSession() {
569 if (!mSessionOpen)
570 return MTP_RESPONSE_SESSION_NOT_OPEN;
571 mSessionID = 0;
572 mSessionOpen = false;
573 mDatabase->sessionEnded();
574 return MTP_RESPONSE_OK;
575}
576
577MtpResponseCode MtpServer::doGetStorageIDs() {
578 MTPD("doGetStorageIDs()\n");
579 if (!mSessionOpen)
580 return MTP_RESPONSE_SESSION_NOT_OPEN;
581 int count = mStorages.size();
582 mData.putUInt32(count);
583 for (int i = 0; i < count; i++) {
584 MTPD("getting storageid %d\n", mStorages[i]->getStorageID());
585 mData.putUInt32(mStorages[i]->getStorageID());
586 }
587
588 return MTP_RESPONSE_OK;
589}
590
591MtpResponseCode MtpServer::doGetStorageInfo() {
592 MtpStringBuffer string;
593 MTPD("doGetStorageInfo()\n");
594 if (!mSessionOpen)
595 return MTP_RESPONSE_SESSION_NOT_OPEN;
596 MtpStorageID id = mRequest.getParameter(1);
597 MtpStorage* storage = getStorage(id);
598 if (!storage) {
599 MTPE("invalid storage id\n");
600 return MTP_RESPONSE_INVALID_STORAGE_ID;
601 }
602
603 mData.putUInt16(storage->getType());
604 mData.putUInt16(storage->getFileSystemType());
605 mData.putUInt16(storage->getAccessCapability());
606 mData.putUInt64(storage->getMaxCapacity());
607 mData.putUInt64(storage->getFreeSpace());
608 mData.putUInt32(1024*1024*1024); // Free Space in Objects
609 string.set(storage->getDescription());
610 mData.putString(string);
611 mData.putEmptyString(); // Volume Identifier
612
613 return MTP_RESPONSE_OK;
614}
615
616MtpResponseCode MtpServer::doGetObjectPropsSupported() {
617 MTPD("doGetObjectPropsSupported()\n");
618 if (!mSessionOpen)
619 return MTP_RESPONSE_SESSION_NOT_OPEN;
620 MtpObjectFormat format = mRequest.getParameter(1);
621 mDatabase->lockMutex();
622 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
623 mData.putAUInt16(properties);
624 delete properties;
625 mDatabase->unlockMutex();
626 return MTP_RESPONSE_OK;
627}
628
629MtpResponseCode MtpServer::doGetObjectHandles() {
630 if (!mSessionOpen)
631 return MTP_RESPONSE_SESSION_NOT_OPEN;
632 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
633 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
634 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
635 // 0x00000000 for all objects
636
637 if (!hasStorage(storageID))
638 return MTP_RESPONSE_INVALID_STORAGE_ID;
639
640 MTPD("calling MtpDatabase->getObjectList()\n");
641 mDatabase->lockMutex();
642 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
643 mData.putAUInt32(handles);
644 delete handles;
645 mDatabase->unlockMutex();
646 return MTP_RESPONSE_OK;
647}
648
649MtpResponseCode MtpServer::doGetNumObjects() {
650 if (!mSessionOpen)
651 return MTP_RESPONSE_SESSION_NOT_OPEN;
652 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
653 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
654 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
655 // 0x00000000 for all objects
656 if (!hasStorage(storageID))
657 return MTP_RESPONSE_INVALID_STORAGE_ID;
658
659 mDatabase->lockMutex();
660 int count = mDatabase->getNumObjects(storageID, format, parent);
661 mDatabase->unlockMutex();
662 if (count >= 0) {
663 mResponse.setParameter(1, count);
664 return MTP_RESPONSE_OK;
665 } else {
666 mResponse.setParameter(1, 0);
667 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
668 }
669}
670
671MtpResponseCode MtpServer::doGetObjectReferences() {
672 if (!mSessionOpen)
673 return MTP_RESPONSE_SESSION_NOT_OPEN;
674 if (!hasStorage())
675 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
676 MtpObjectHandle handle = mRequest.getParameter(1);
677
678 // FIXME - check for invalid object handle
679 mDatabase->lockMutex();
680 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
681 if (handles) {
682 mData.putAUInt32(handles);
683 delete handles;
684 } else {
685 MTPD("MtpServer::doGetObjectReferences putEmptyArray\n");
686 mData.putEmptyArray();
687 }
688 mDatabase->unlockMutex();
689 return MTP_RESPONSE_OK;
690}
691
692MtpResponseCode MtpServer::doSetObjectReferences() {
693 if (!mSessionOpen)
694 return MTP_RESPONSE_SESSION_NOT_OPEN;
695 if (!hasStorage())
696 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
697 MtpStorageID handle = mRequest.getParameter(1);
698
699 MtpObjectHandleList* references = mData.getAUInt32();
700 mDatabase->lockMutex();
701 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
702 mDatabase->unlockMutex();
703 delete references;
704 return result;
705}
706
707MtpResponseCode MtpServer::doGetObjectPropValue() {
708 if (!hasStorage())
709 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
710 MtpObjectHandle handle = mRequest.getParameter(1);
711 MtpObjectProperty property = mRequest.getParameter(2);
712 MTPD("GetObjectPropValue %d %s\n", handle,
713 MtpDebug::getObjectPropCodeName(property));
714
715 mDatabase->lockMutex();
716 MtpResponseCode res = mDatabase->getObjectPropertyValue(handle, property, mData);
717 mDatabase->unlockMutex();
718 return res;
719}
720
721MtpResponseCode MtpServer::doSetObjectPropValue() {
722 if (!hasStorage())
723 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
724 MtpObjectHandle handle = mRequest.getParameter(1);
725 MtpObjectProperty property = mRequest.getParameter(2);
726 MTPD("SetObjectPropValue %d %s\n", handle,
727 MtpDebug::getObjectPropCodeName(property));
728
729 mDatabase->lockMutex();
730 MtpResponseCode res = mDatabase->setObjectPropertyValue(handle, property, mData);
731 mDatabase->unlockMutex();
732 return res;
733}
734
735MtpResponseCode MtpServer::doGetDevicePropValue() {
736 MtpDeviceProperty property = mRequest.getParameter(1);
737 MTPD("GetDevicePropValue %s\n",
738 MtpDebug::getDevicePropCodeName(property));
739
740 mDatabase->lockMutex();
741 MtpResponseCode res = mDatabase->getDevicePropertyValue(property, mData);
742 mDatabase->unlockMutex();
743 return res;
744}
745
746MtpResponseCode MtpServer::doSetDevicePropValue() {
747 MtpDeviceProperty property = mRequest.getParameter(1);
748 MTPD("SetDevicePropValue %s\n",
749 MtpDebug::getDevicePropCodeName(property));
750
751 mDatabase->lockMutex();
752 MtpResponseCode res = mDatabase->setDevicePropertyValue(property, mData);
753 mDatabase->unlockMutex();
754 return res;
755}
756
757MtpResponseCode MtpServer::doResetDevicePropValue() {
758 MtpDeviceProperty property = mRequest.getParameter(1);
759 MTPD("ResetDevicePropValue %s\n",
760 MtpDebug::getDevicePropCodeName(property));
761
762 mDatabase->lockMutex();
763 MtpResponseCode res = mDatabase->resetDeviceProperty(property);
764 mDatabase->unlockMutex();
765 return res;
766}
767
768MtpResponseCode MtpServer::doGetObjectPropList() {
769 if (!hasStorage())
770 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
771
772 MtpObjectHandle handle = mRequest.getParameter(1);
773 // use uint32_t so we can support 0xFFFFFFFF
774 uint32_t format = mRequest.getParameter(2);
775 uint32_t property = mRequest.getParameter(3);
776 int groupCode = mRequest.getParameter(4);
777 int depth = mRequest.getParameter(5);
778 MTPD("GetObjectPropList %d format: %s property: %x group: %d depth: %d\n",
779 handle, MtpDebug::getFormatCodeName(format),
780 property, groupCode, depth);
781
782 mDatabase->lockMutex();
783 MtpResponseCode res = mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
784 mDatabase->unlockMutex();
785 return res;
786}
787
788MtpResponseCode MtpServer::doGetObjectInfo() {
789 MTPD("inside doGetObjectInfo()\n");
790 if (!hasStorage())
791 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
792 MtpObjectHandle handle = mRequest.getParameter(1);
793 MtpObjectInfo info(handle);
794 MTPD("calling mtpdatabase getObjectInfo()\n");
795 mDatabase->lockMutex();
796 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
797 mDatabase->unlockMutex();
798 if (result == MTP_RESPONSE_OK) {
799 char date[20];
800
801 mData.putUInt32(info.mStorageID);
802 mData.putUInt16(info.mFormat);
803 mData.putUInt16(info.mProtectionStatus);
804
805 // if object is being edited the database size may be out of date
806 uint32_t size = info.mCompressedSize;
807 ObjectEdit* edit = getEditObject(handle);
808 if (edit)
809 size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
810 mData.putUInt32(size);
811
812 mData.putUInt16(info.mThumbFormat);
813 mData.putUInt32(info.mThumbCompressedSize);
814 mData.putUInt32(info.mThumbPixWidth);
815 mData.putUInt32(info.mThumbPixHeight);
816 mData.putUInt32(info.mImagePixWidth);
817 mData.putUInt32(info.mImagePixHeight);
818 mData.putUInt32(info.mImagePixDepth);
819 mData.putUInt32(info.mParent);
820 mData.putUInt16(info.mAssociationType);
821 mData.putUInt32(info.mAssociationDesc);
822 mData.putUInt32(info.mSequenceNumber);
823 MTPD("info.mName: %s\n", info.mName);
824 mData.putString(info.mName);
825 mData.putEmptyString(); // date created
826 formatDateTime(info.mDateModified, date, sizeof(date));
827 mData.putString(date); // date modified
828 mData.putEmptyString(); // keywords
829 }
830 return result;
831}
832
833MtpResponseCode MtpServer::doGetObject() {
834 if (!hasStorage())
835 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
836 MtpObjectHandle handle = mRequest.getParameter(1);
837 MtpString pathBuf;
838 int64_t fileLength;
839 MtpObjectFormat format;
840 MTPD("MtpServer::doGetObject calling getObjectFilePath\n");
841 mDatabase->lockMutex();
842 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
843 mDatabase->unlockMutex();
844 if (result != MTP_RESPONSE_OK)
845 return result;
846
847 const char* filePath = (const char *)pathBuf;
848 MTPD("filePath: %s\n", filePath);
849 mtp_file_range mfr;
850 mfr.fd = open(filePath, O_RDONLY);
851 if (mfr.fd < 0) {
852 return MTP_RESPONSE_GENERAL_ERROR;
853 }
854 mfr.offset = 0;
855 mfr.length = fileLength;
856 MTPD("mfr.length: %lld\n", mfr.length);
857 mfr.command = mRequest.getOperationCode();
858 mfr.transaction_id = mRequest.getTransactionID();
859
860 // then transfer the file
861 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
862 MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
863 close(mfr.fd);
864 if (ret < 0) {
865 if (errno == ECANCELED)
866 return MTP_RESPONSE_TRANSACTION_CANCELLED;
867 else
868 return MTP_RESPONSE_GENERAL_ERROR;
869 }
870 return MTP_RESPONSE_OK;
871}
872
873MtpResponseCode MtpServer::doGetThumb() {
874 MtpObjectHandle handle = mRequest.getParameter(1);
875 size_t thumbSize;
876 mDatabase->lockMutex();
877 void* thumb = mDatabase->getThumbnail(handle, thumbSize);
878 mDatabase->unlockMutex();
879 if (thumb) {
880 // send data
881 mData.setOperationCode(mRequest.getOperationCode());
882 mData.setTransactionID(mRequest.getTransactionID());
883 mData.writeData(mFD, thumb, thumbSize);
884 free(thumb);
885 return MTP_RESPONSE_OK;
886 } else {
887 return MTP_RESPONSE_GENERAL_ERROR;
888 }
889}
890
891MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
892 if (!hasStorage())
893 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
894 MtpObjectHandle handle = mRequest.getParameter(1);
895 uint64_t offset;
896 uint32_t length;
897 offset = mRequest.getParameter(2);
898 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
899 // android extension with 64 bit offset
900 uint64_t offset2 = mRequest.getParameter(3);
901 offset = offset | (offset2 << 32);
902 length = mRequest.getParameter(4);
903 } else {
904 // standard GetPartialObject
905 length = mRequest.getParameter(3);
906 }
907 MtpString pathBuf;
908 int64_t fileLength;
909 MtpObjectFormat format;
910 MTPD("MtpServer::doGetPartialObject calling getObjectFilePath\n");
911 mDatabase->lockMutex();
912 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
913 mDatabase->unlockMutex();
914 if (result != MTP_RESPONSE_OK) {
915 return result;
916 }
917 if (offset + length > (uint64_t)fileLength)
918 length = fileLength - offset;
919
920 const char* filePath = (const char *)pathBuf;
921 mtp_file_range mfr;
922 mfr.fd = open(filePath, O_RDONLY);
923 if (mfr.fd < 0) {
924 return MTP_RESPONSE_GENERAL_ERROR;
925 }
926 mfr.offset = offset;
927 mfr.length = length;
928 mfr.command = mRequest.getOperationCode();
929 mfr.transaction_id = mRequest.getTransactionID();
930 mResponse.setParameter(1, length);
931
932 // transfer the file
933 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
934 MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
935 close(mfr.fd);
936 if (ret < 0) {
937 if (errno == ECANCELED)
938 return MTP_RESPONSE_TRANSACTION_CANCELLED;
939 else
940 return MTP_RESPONSE_GENERAL_ERROR;
941 }
942 return MTP_RESPONSE_OK;
943}
944
945MtpResponseCode MtpServer::doSendObjectInfo() {
946 MTPD("MtpServer::doSendObjectInfo starting\n");
947 MtpString path;
948 MtpStorageID storageID = mRequest.getParameter(1);
949 MtpStorage* storage = getStorage(storageID);
950 MtpObjectHandle parent = mRequest.getParameter(2);
951 if (!storage)
952 return MTP_RESPONSE_INVALID_STORAGE_ID;
953
954 // special case the root
955 if (parent == MTP_PARENT_ROOT) {
956 MTPD("MtpServer::doSendObjectInfo special case root\n");
957 path = storage->getPath();
958 parent = 0;
959 } else {
960 int64_t length;
961 MtpObjectFormat format;
962 MTPD("MtpServer::doSendObjectInfo calling getObjectFilePath\n");
963 mDatabase->lockMutex();
964 int result = mDatabase->getObjectFilePath(parent, path, length, format);
965 mDatabase->unlockMutex();
966 if (result != MTP_RESPONSE_OK) {
967 return result;
968 }
969 if (format != MTP_FORMAT_ASSOCIATION)
970 return MTP_RESPONSE_INVALID_PARENT_OBJECT;
971 }
972
973 // read only the fields we need
974 mData.getUInt32(); // storage ID
975 MtpObjectFormat format = mData.getUInt16();
976 mData.getUInt16(); // protection status
977 mSendObjectFileSize = mData.getUInt32();
978 mData.getUInt16(); // thumb format
979 mData.getUInt32(); // thumb compressed size
980 mData.getUInt32(); // thumb pix width
981 mData.getUInt32(); // thumb pix height
982 mData.getUInt32(); // image pix width
983 mData.getUInt32(); // image pix height
984 mData.getUInt32(); // image bit depth
985 mData.getUInt32(); // parent
986 uint16_t associationType = mData.getUInt16();
987 uint32_t associationDesc = mData.getUInt32(); // association desc
988 mData.getUInt32(); // sequence number
989 MtpStringBuffer name, created, modified;
990 mData.getString(name); // file name
991 mData.getString(created); // date created
992 mData.getString(modified); // date modified
993 // keywords follow
994
995 MTPD("name: %s format: %04X\n", (const char *)name, format);
996 time_t modifiedTime;
997 if (!parseDateTime(modified, modifiedTime)) {
998 modifiedTime = 0;
999 }
1000 if (path[path.size() - 1] != '/') {
1001 path += "/";
1002 }
1003 path += (const char *)name;
1004
1005 // check space first
1006 if (mSendObjectFileSize > storage->getFreeSpace())
1007 return MTP_RESPONSE_STORAGE_FULL;
1008 uint64_t maxFileSize = storage->getMaxFileSize();
1009 // check storage max file size
bigbiff0c532032014-12-21 13:41:26 -05001010 MTPD("maxFileSize: %ld\n", maxFileSize);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001011 if (maxFileSize != 0) {
1012 // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
1013 // is >= 0xFFFFFFFF
1014 if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
1015 return MTP_RESPONSE_OBJECT_TOO_LARGE;
1016 }
1017
1018 MTPD("MtpServer::doSendObjectInfo path: %s parent: %d storageID: %08X\n", (const char*)path, parent, storageID);
1019 mDatabase->lockMutex();
1020 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
1021 format, parent, storageID, mSendObjectFileSize, modifiedTime);
1022 mDatabase->unlockMutex();
1023 if (handle == kInvalidObjectHandle) {
1024 MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, handle == kInvalidObjectHandle\n");
1025 return MTP_RESPONSE_GENERAL_ERROR;
1026 }
1027
1028 if (format == MTP_FORMAT_ASSOCIATION) {
1029 mode_t mask = umask(0);
1030 MTPD("MtpServer::doSendObjectInfo mkdir '%s'\n", (const char *)path);
1031 int ret = mkdir((const char *)path, mDirectoryPermission);
1032 umask(mask);
1033 if (ret && ret != -EEXIST) {
1034 MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, ret && ret != -EEXIST\n");
1035 return MTP_RESPONSE_GENERAL_ERROR;
1036 }
1037 chown((const char *)path, getuid(), mFileGroup);
Ethan Yonker4b94cfd2014-12-11 10:00:45 -06001038 tw_set_default_metadata((const char *)path);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001039
1040 // SendObject does not get sent for directories, so call endSendObject here instead
1041 mDatabase->lockMutex();
1042 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
1043 mDatabase->unlockMutex();
1044 } else {
1045 mSendObjectFilePath = path;
1046 // save the handle for the SendObject call, which should follow
1047 mSendObjectHandle = handle;
1048 mSendObjectFormat = format;
1049 }
1050
1051 mResponse.setParameter(1, storageID);
1052 mResponse.setParameter(2, parent);
1053 mResponse.setParameter(3, handle);
1054 MTPD("MtpServer::doSendObjectInfo returning MTP_RESPONSE_OK\n");
1055 return MTP_RESPONSE_OK;
1056}
1057
1058MtpResponseCode MtpServer::doSendObject() {
1059 if (!hasStorage())
1060 return MTP_RESPONSE_GENERAL_ERROR;
1061 MtpResponseCode result = MTP_RESPONSE_OK;
1062 mode_t mask;
1063 int ret = 0, initialData;
1064
1065 if (mSendObjectHandle == kInvalidObjectHandle) {
1066 MTPE("Expected SendObjectInfo before SendObject");
1067 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
1068 goto done;
1069 }
1070
1071 // read the header, and possibly some data
1072 ret = mData.read(mFD);
1073 if (ret < MTP_CONTAINER_HEADER_SIZE) {
1074 MTPE("MTP_RESPONSE_GENERAL_ERROR\n");
1075 result = MTP_RESPONSE_GENERAL_ERROR;
1076 goto done;
1077 }
1078 initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1079
1080 mtp_file_range mfr;
1081 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, 0640);
1082 if (mfr.fd < 0) {
1083 result = MTP_RESPONSE_GENERAL_ERROR;
1084 MTPE("fd error\n");
1085 goto done;
1086 }
1087 fchown(mfr.fd, getuid(), mFileGroup);
1088 // set permissions
1089 mask = umask(0);
1090 fchmod(mfr.fd, mFilePermission);
1091 umask(mask);
1092
1093 if (initialData > 0)
1094 ret = write(mfr.fd, mData.getData(), initialData);
1095
1096 if (mSendObjectFileSize - initialData > 0) {
1097 mfr.offset = initialData;
1098 if (mSendObjectFileSize == 0xFFFFFFFF) {
1099 // tell driver to read until it receives a short packet
1100 mfr.length = 0xFFFFFFFF;
1101 } else {
1102 mfr.length = mSendObjectFileSize - initialData;
1103 }
1104
1105 MTPD("receiving %s\n", (const char *)mSendObjectFilePath);
1106 // transfer the file
1107 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1108 }
1109 close(mfr.fd);
Ethan Yonker4b94cfd2014-12-11 10:00:45 -06001110 tw_set_default_metadata((const char *)mSendObjectFilePath);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001111
1112 if (ret < 0) {
1113 unlink(mSendObjectFilePath);
1114 if (errno == ECANCELED)
1115 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
bigbiff7cb4c332014-11-26 20:36:07 -05001116 else {
1117 MTPD("errno: %d\n", errno);
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001118 result = MTP_RESPONSE_GENERAL_ERROR;
bigbiff7cb4c332014-11-26 20:36:07 -05001119 }
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001120 }
1121
1122done:
1123 // reset so we don't attempt to send the data back
1124 MTPD("MTP_RECEIVE_FILE returned %d\n", ret);
1125 mData.reset();
1126 mDatabase->lockMutex();
1127 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
1128 result == MTP_RESPONSE_OK);
1129 mDatabase->unlockMutex();
1130 mSendObjectHandle = kInvalidObjectHandle;
1131 MTPD("result: %d\n", result);
1132 mSendObjectFormat = 0;
bigbiff7cb4c332014-11-26 20:36:07 -05001133 return result;
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001134}
1135
1136static void deleteRecursive(const char* path) {
1137 char pathbuf[PATH_MAX];
1138 size_t pathLength = strlen(path);
1139 if (pathLength >= sizeof(pathbuf) - 1) {
1140 MTPE("path too long: %s\n", path);
1141 }
1142 strcpy(pathbuf, path);
1143 if (pathbuf[pathLength - 1] != '/') {
1144 pathbuf[pathLength++] = '/';
1145 }
1146 char* fileSpot = pathbuf + pathLength;
1147 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
1148
1149 DIR* dir = opendir(path);
1150 if (!dir) {
1151 MTPE("opendir %s failed: %s", path, strerror(errno));
1152 return;
1153 }
1154
1155 struct dirent* entry;
1156 while ((entry = readdir(dir))) {
1157 const char* name = entry->d_name;
1158
1159 // ignore "." and ".."
1160 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1161 continue;
1162 }
1163
1164 int nameLength = strlen(name);
1165 if (nameLength > pathRemaining) {
1166 MTPE("path %s/%s too long\n", path, name);
1167 continue;
1168 }
1169 strcpy(fileSpot, name);
1170
1171 int type = entry->d_type;
Ethan Yonker241a3ce2014-09-04 12:59:27 -05001172 struct stat st;
1173 if (lstat(pathbuf, &st)) {
1174 MTPE("Failed to lstat '%s'\n", pathbuf);
1175 continue;
1176 }
1177 if (st.st_mode & S_IFDIR) {
bigbiff bigbiffc7eee6f2014-09-02 18:59:01 -04001178 deleteRecursive(pathbuf);
1179 rmdir(pathbuf);
1180 } else {
1181 unlink(pathbuf);
1182 }
1183 }
1184 closedir(dir);
1185}
1186
1187static void deletePath(const char* path) {
1188 struct stat statbuf;
1189 if (stat(path, &statbuf) == 0) {
1190 if (S_ISDIR(statbuf.st_mode)) {
1191 deleteRecursive(path);
1192 rmdir(path);
1193 } else {
1194 unlink(path);
1195 }
1196 } else {
1197 MTPE("deletePath stat failed for %s: %s", path, strerror(errno));
1198 }
1199}
1200
1201MtpResponseCode MtpServer::doDeleteObject() {
1202 if (!hasStorage())
1203 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1204 MtpObjectHandle handle = mRequest.getParameter(1);
1205 MtpObjectFormat format = mRequest.getParameter(2);
1206 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1207 // FIXME - implement deleting objects by format
1208
1209 MtpString filePath;
1210 int64_t fileLength;
1211 MTPD("MtpServer::doDeleteObject calling getObjectFilePath\n");
1212 mDatabase->lockMutex();
1213 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
1214 if (result == MTP_RESPONSE_OK) {
1215 MTPD("deleting %s", (const char *)filePath);
1216 result = mDatabase->deleteFile(handle);
1217 // Don't delete the actual files unless the database deletion is allowed
1218 if (result == MTP_RESPONSE_OK) {
1219 deletePath((const char *)filePath);
1220 }
1221 }
1222 mDatabase->unlockMutex();
1223 return result;
1224}
1225
1226MtpResponseCode MtpServer::doGetObjectPropDesc() {
1227 MtpObjectProperty propCode = mRequest.getParameter(1);
1228 MtpObjectFormat format = mRequest.getParameter(2);
1229 MTPD("MtpServer::doGetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
1230 MtpDebug::getFormatCodeName(format));
1231 mDatabase->lockMutex();
1232 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
1233 mDatabase->unlockMutex();
1234 if (!property) {
1235 MTPE("MtpServer::doGetObjectPropDesc propery not supported\n");
1236 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
1237 }
1238 property->write(mData);
1239 delete property;
1240 return MTP_RESPONSE_OK;
1241}
1242
1243MtpResponseCode MtpServer::doGetDevicePropDesc() {
1244 MtpDeviceProperty propCode = mRequest.getParameter(1);
1245 MTPD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
1246 mDatabase->lockMutex();
1247 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1248 mDatabase->unlockMutex();
1249 if (!property) {
1250 MTPE("MtpServer::doGetDevicePropDesc property not supported\n");
1251 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1252 }
1253 property->write(mData);
1254 delete property;
1255 return MTP_RESPONSE_OK;
1256}
1257
1258MtpResponseCode MtpServer::doSendPartialObject() {
1259 if (!hasStorage())
1260 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1261 MtpObjectHandle handle = mRequest.getParameter(1);
1262 uint64_t offset = mRequest.getParameter(2);
1263 uint64_t offset2 = mRequest.getParameter(3);
1264 offset = offset | (offset2 << 32);
1265 uint32_t length = mRequest.getParameter(4);
1266
1267 ObjectEdit* edit = getEditObject(handle);
1268 if (!edit) {
1269 MTPE("object not open for edit in doSendPartialObject");
1270 return MTP_RESPONSE_GENERAL_ERROR;
1271 }
1272
1273 // can't start writing past the end of the file
1274 if (offset > edit->mSize) {
1275 MTPE("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
1276 return MTP_RESPONSE_GENERAL_ERROR;
1277 }
1278
1279 const char* filePath = (const char *)edit->mPath;
1280 MTPD("receiving partial %s %lld %lld\n", filePath, offset, length);
1281
1282 // read the header, and possibly some data
1283 int ret = mData.read(mFD);
1284 if (ret < MTP_CONTAINER_HEADER_SIZE)
1285 return MTP_RESPONSE_GENERAL_ERROR;
1286 int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1287
1288 if (initialData > 0) {
1289 ret = write(edit->mFD, mData.getData(), initialData);
1290 offset += initialData;
1291 length -= initialData;
1292 }
1293
1294 if (length > 0) {
1295 mtp_file_range mfr;
1296 mfr.fd = edit->mFD;
1297 mfr.offset = offset;
1298 mfr.length = length;
1299
1300 // transfer the file
1301 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1302 MTPD("MTP_RECEIVE_FILE returned %d", ret);
1303 }
1304 if (ret < 0) {
1305 mResponse.setParameter(1, 0);
1306 if (errno == ECANCELED)
1307 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1308 else
1309 return MTP_RESPONSE_GENERAL_ERROR;
1310 }
1311
1312 // reset so we don't attempt to send this back
1313 mData.reset();
1314 mResponse.setParameter(1, length);
1315 uint64_t end = offset + length;
1316 if (end > edit->mSize) {
1317 edit->mSize = end;
1318 }
1319 return MTP_RESPONSE_OK;
1320}
1321
1322MtpResponseCode MtpServer::doTruncateObject() {
1323 MtpObjectHandle handle = mRequest.getParameter(1);
1324 ObjectEdit* edit = getEditObject(handle);
1325 if (!edit) {
1326 MTPE("object not open for edit in doTruncateObject");
1327 return MTP_RESPONSE_GENERAL_ERROR;
1328 }
1329
1330 uint64_t offset = mRequest.getParameter(2);
1331 uint64_t offset2 = mRequest.getParameter(3);
1332 offset |= (offset2 << 32);
1333 if (ftruncate(edit->mFD, offset) != 0) {
1334 return MTP_RESPONSE_GENERAL_ERROR;
1335 } else {
1336 edit->mSize = offset;
1337 return MTP_RESPONSE_OK;
1338 }
1339}
1340
1341MtpResponseCode MtpServer::doBeginEditObject() {
1342 MtpObjectHandle handle = mRequest.getParameter(1);
1343 if (getEditObject(handle)) {
1344 MTPE("object already open for edit in doBeginEditObject");
1345 return MTP_RESPONSE_GENERAL_ERROR;
1346 }
1347
1348 MtpString path;
1349 int64_t fileLength;
1350 MtpObjectFormat format;
1351 MTPD("MtpServer::doBeginEditObject calling getObjectFilePath\n");
1352 mDatabase->lockMutex();
1353 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1354 mDatabase->unlockMutex();
1355 if (result != MTP_RESPONSE_OK)
1356 return result;
1357
1358 int fd = open((const char *)path, O_RDWR | O_EXCL);
1359 if (fd < 0) {
1360 MTPE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1361 return MTP_RESPONSE_GENERAL_ERROR;
1362 }
1363
1364 addEditObject(handle, path, fileLength, format, fd);
1365 return MTP_RESPONSE_OK;
1366}
1367
1368MtpResponseCode MtpServer::doEndEditObject() {
1369 MtpObjectHandle handle = mRequest.getParameter(1);
1370 ObjectEdit* edit = getEditObject(handle);
1371 if (!edit) {
1372 MTPE("object not open for edit in doEndEditObject");
1373 return MTP_RESPONSE_GENERAL_ERROR;
1374 }
1375
1376 commitEdit(edit);
1377 removeEditObject(handle);
1378 return MTP_RESPONSE_OK;
1379}