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