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