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