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