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