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