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