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