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