MtpServer.cpp revision 8d08c5aeee05425357b6603a22fa73fd6e095a47
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <sys/types.h> 20#include <sys/ioctl.h> 21#include <sys/stat.h> 22#include <fcntl.h> 23#include <errno.h> 24#include <sys/stat.h> 25#include <dirent.h> 26 27#include <cutils/properties.h> 28 29#define LOG_TAG "MtpServer" 30 31#include "MtpDebug.h" 32#include "MtpDatabase.h" 33#include "MtpProperty.h" 34#include "MtpServer.h" 35#include "MtpStorage.h" 36#include "MtpStringBuffer.h" 37 38#include <linux/usb/f_mtp.h> 39 40namespace android { 41 42static const MtpOperationCode kSupportedOperationCodes[] = { 43 MTP_OPERATION_GET_DEVICE_INFO, 44 MTP_OPERATION_OPEN_SESSION, 45 MTP_OPERATION_CLOSE_SESSION, 46 MTP_OPERATION_GET_STORAGE_IDS, 47 MTP_OPERATION_GET_STORAGE_INFO, 48 MTP_OPERATION_GET_NUM_OBJECTS, 49 MTP_OPERATION_GET_OBJECT_HANDLES, 50 MTP_OPERATION_GET_OBJECT_INFO, 51 MTP_OPERATION_GET_OBJECT, 52// MTP_OPERATION_GET_THUMB, 53 MTP_OPERATION_DELETE_OBJECT, 54 MTP_OPERATION_SEND_OBJECT_INFO, 55 MTP_OPERATION_SEND_OBJECT, 56// MTP_OPERATION_INITIATE_CAPTURE, 57// MTP_OPERATION_FORMAT_STORE, 58// MTP_OPERATION_RESET_DEVICE, 59// MTP_OPERATION_SELF_TEST, 60// MTP_OPERATION_SET_OBJECT_PROTECTION, 61// MTP_OPERATION_POWER_DOWN, 62 MTP_OPERATION_GET_DEVICE_PROP_DESC, 63 MTP_OPERATION_GET_DEVICE_PROP_VALUE, 64 MTP_OPERATION_SET_DEVICE_PROP_VALUE, 65 MTP_OPERATION_RESET_DEVICE_PROP_VALUE, 66// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, 67// MTP_OPERATION_MOVE_OBJECT, 68// MTP_OPERATION_COPY_OBJECT, 69 MTP_OPERATION_GET_PARTIAL_OBJECT, 70// MTP_OPERATION_INITIATE_OPEN_CAPTURE, 71 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, 72 MTP_OPERATION_GET_OBJECT_PROP_DESC, 73 MTP_OPERATION_GET_OBJECT_PROP_VALUE, 74 MTP_OPERATION_SET_OBJECT_PROP_VALUE, 75 MTP_OPERATION_GET_OBJECT_PROP_LIST, 76// MTP_OPERATION_SET_OBJECT_PROP_LIST, 77// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, 78// MTP_OPERATION_SEND_OBJECT_PROP_LIST, 79 MTP_OPERATION_GET_OBJECT_REFERENCES, 80 MTP_OPERATION_SET_OBJECT_REFERENCES, 81// MTP_OPERATION_SKIP, 82}; 83 84static const MtpEventCode kSupportedEventCodes[] = { 85 MTP_EVENT_OBJECT_ADDED, 86 MTP_EVENT_OBJECT_REMOVED, 87}; 88 89MtpServer::MtpServer(int fd, MtpDatabase* database, 90 int fileGroup, int filePerm, int directoryPerm) 91 : mFD(fd), 92 mDatabase(database), 93 mFileGroup(fileGroup), 94 mFilePermission(filePerm), 95 mDirectoryPermission(directoryPerm), 96 mSessionID(0), 97 mSessionOpen(false), 98 mSendObjectHandle(kInvalidObjectHandle), 99 mSendObjectFormat(0), 100 mSendObjectFileSize(0) 101{ 102} 103 104MtpServer::~MtpServer() { 105} 106 107void MtpServer::addStorage(const char* filePath, uint64_t reserveSpace) { 108 int index = mStorages.size() + 1; 109 index |= index << 16; // set high and low part to our index 110 MtpStorage* storage = new MtpStorage(index, filePath, reserveSpace); 111 addStorage(storage); 112} 113 114MtpStorage* MtpServer::getStorage(MtpStorageID id) { 115 if (id == 0) 116 return mStorages[0]; 117 for (int i = 0; i < mStorages.size(); i++) { 118 MtpStorage* storage = mStorages[i]; 119 if (storage->getStorageID() == id) 120 return storage; 121 } 122 return NULL; 123} 124 125void MtpServer::run() { 126 int fd = mFD; 127 128 LOGV("MtpServer::run fd: %d\n", fd); 129 130 while (1) { 131 int ret = mRequest.read(fd); 132 if (ret < 0) { 133 LOGV("request read returned %d, errno: %d", ret, errno); 134 if (errno == ECANCELED) { 135 // return to top of loop and wait for next command 136 continue; 137 } 138 break; 139 } 140 MtpOperationCode operation = mRequest.getOperationCode(); 141 MtpTransactionID transaction = mRequest.getTransactionID(); 142 143 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation)); 144 mRequest.dump(); 145 146 // FIXME need to generalize this 147 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO 148 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES 149 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE 150 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE); 151 if (dataIn) { 152 int ret = mData.read(fd); 153 if (ret < 0) { 154 LOGE("data read returned %d, errno: %d", ret, errno); 155 if (errno == ECANCELED) { 156 // return to top of loop and wait for next command 157 continue; 158 } 159 break; 160 } 161 LOGV("received data:"); 162 mData.dump(); 163 } else { 164 mData.reset(); 165 } 166 167 if (handleRequest()) { 168 if (!dataIn && mData.hasData()) { 169 mData.setOperationCode(operation); 170 mData.setTransactionID(transaction); 171 LOGV("sending data:"); 172 mData.dump(); 173 ret = mData.write(fd); 174 if (ret < 0) { 175 LOGE("request write returned %d, errno: %d", ret, errno); 176 if (errno == ECANCELED) { 177 // return to top of loop and wait for next command 178 continue; 179 } 180 break; 181 } 182 } 183 184 mResponse.setTransactionID(transaction); 185 LOGV("sending response %04X", mResponse.getResponseCode()); 186 ret = mResponse.write(fd); 187 mResponse.dump(); 188 if (ret < 0) { 189 LOGE("request write returned %d, errno: %d", ret, errno); 190 if (errno == ECANCELED) { 191 // return to top of loop and wait for next command 192 continue; 193 } 194 break; 195 } 196 } else { 197 LOGV("skipping response\n"); 198 } 199 } 200 201 if (mSessionOpen) 202 mDatabase->sessionEnded(); 203} 204 205void MtpServer::sendObjectAdded(MtpObjectHandle handle) { 206 if (mSessionOpen) { 207 LOGV("sendObjectAdded %d\n", handle); 208 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED); 209 mEvent.setTransactionID(mRequest.getTransactionID()); 210 mEvent.setParameter(1, handle); 211 int ret = mEvent.write(mFD); 212 LOGV("mEvent.write returned %d\n", ret); 213 } 214} 215 216void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 217 if (mSessionOpen) { 218 LOGV("sendObjectRemoved %d\n", handle); 219 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED); 220 mEvent.setTransactionID(mRequest.getTransactionID()); 221 mEvent.setParameter(1, handle); 222 int ret = mEvent.write(mFD); 223 LOGV("mEvent.write returned %d\n", ret); 224 } 225} 226 227bool MtpServer::handleRequest() { 228 MtpOperationCode operation = mRequest.getOperationCode(); 229 MtpResponseCode response; 230 231 mResponse.reset(); 232 233 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 234 // FIXME - need to delete mSendObjectHandle from the database 235 LOGE("expected SendObject after SendObjectInfo"); 236 mSendObjectHandle = kInvalidObjectHandle; 237 } 238 239 switch (operation) { 240 case MTP_OPERATION_GET_DEVICE_INFO: 241 response = doGetDeviceInfo(); 242 break; 243 case MTP_OPERATION_OPEN_SESSION: 244 response = doOpenSession(); 245 break; 246 case MTP_OPERATION_CLOSE_SESSION: 247 response = doCloseSession(); 248 break; 249 case MTP_OPERATION_GET_STORAGE_IDS: 250 response = doGetStorageIDs(); 251 break; 252 case MTP_OPERATION_GET_STORAGE_INFO: 253 response = doGetStorageInfo(); 254 break; 255 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 256 response = doGetObjectPropsSupported(); 257 break; 258 case MTP_OPERATION_GET_OBJECT_HANDLES: 259 response = doGetObjectHandles(); 260 break; 261 case MTP_OPERATION_GET_NUM_OBJECTS: 262 response = doGetNumObjects(); 263 break; 264 case MTP_OPERATION_GET_OBJECT_REFERENCES: 265 response = doGetObjectReferences(); 266 break; 267 case MTP_OPERATION_SET_OBJECT_REFERENCES: 268 response = doSetObjectReferences(); 269 break; 270 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 271 response = doGetObjectPropValue(); 272 break; 273 case MTP_OPERATION_SET_OBJECT_PROP_VALUE: 274 response = doSetObjectPropValue(); 275 break; 276 case MTP_OPERATION_GET_DEVICE_PROP_VALUE: 277 response = doGetDevicePropValue(); 278 break; 279 case MTP_OPERATION_SET_DEVICE_PROP_VALUE: 280 response = doSetDevicePropValue(); 281 break; 282 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: 283 response = doResetDevicePropValue(); 284 break; 285 case MTP_OPERATION_GET_OBJECT_PROP_LIST: 286 response = doGetObjectPropList(); 287 break; 288 case MTP_OPERATION_GET_OBJECT_INFO: 289 response = doGetObjectInfo(); 290 break; 291 case MTP_OPERATION_GET_OBJECT: 292 response = doGetObject(); 293 break; 294 case MTP_OPERATION_GET_PARTIAL_OBJECT: 295 response = doGetPartialObject(); 296 break; 297 case MTP_OPERATION_SEND_OBJECT_INFO: 298 response = doSendObjectInfo(); 299 break; 300 case MTP_OPERATION_SEND_OBJECT: 301 response = doSendObject(); 302 break; 303 case MTP_OPERATION_DELETE_OBJECT: 304 response = doDeleteObject(); 305 break; 306 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 307 response = doGetObjectPropDesc(); 308 break; 309 case MTP_OPERATION_GET_DEVICE_PROP_DESC: 310 response = doGetDevicePropDesc(); 311 break; 312 default: 313 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); 314 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 315 break; 316 } 317 318 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 319 return false; 320 mResponse.setResponseCode(response); 321 return true; 322} 323 324MtpResponseCode MtpServer::doGetDeviceInfo() { 325 MtpStringBuffer string; 326 char prop_value[PROPERTY_VALUE_MAX]; 327 328 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats(); 329 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats(); 330 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties(); 331 332 // fill in device info 333 mData.putUInt16(MTP_STANDARD_VERSION); 334 mData.putUInt32(6); // MTP Vendor Extension ID 335 mData.putUInt16(MTP_STANDARD_VERSION); 336 string.set("microsoft.com: 1.0;"); 337 mData.putString(string); // MTP Extensions 338 mData.putUInt16(0); //Functional Mode 339 mData.putAUInt16(kSupportedOperationCodes, 340 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 341 mData.putAUInt16(kSupportedEventCodes, 342 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported 343 mData.putAUInt16(deviceProperties); // Device Properties Supported 344 mData.putAUInt16(captureFormats); // Capture Formats 345 mData.putAUInt16(playbackFormats); // Playback Formats 346 347 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer"); 348 string.set(prop_value); 349 mData.putString(string); // Manufacturer 350 351 property_get("ro.product.model", prop_value, "MTP Device"); 352 string.set(prop_value); 353 mData.putString(string); // Model 354 string.set("1.0"); 355 mData.putString(string); // Device Version 356 357 property_get("ro.serialno", prop_value, "????????"); 358 string.set(prop_value); 359 mData.putString(string); // Serial Number 360 361 delete playbackFormats; 362 delete captureFormats; 363 delete deviceProperties; 364 365 return MTP_RESPONSE_OK; 366} 367 368MtpResponseCode MtpServer::doOpenSession() { 369 if (mSessionOpen) { 370 mResponse.setParameter(1, mSessionID); 371 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 372 } 373 mSessionID = mRequest.getParameter(1); 374 mSessionOpen = true; 375 376 mDatabase->sessionStarted(); 377 378 return MTP_RESPONSE_OK; 379} 380 381MtpResponseCode MtpServer::doCloseSession() { 382 if (!mSessionOpen) 383 return MTP_RESPONSE_SESSION_NOT_OPEN; 384 mSessionID = 0; 385 mSessionOpen = false; 386 mDatabase->sessionEnded(); 387 return MTP_RESPONSE_OK; 388} 389 390MtpResponseCode MtpServer::doGetStorageIDs() { 391 if (!mSessionOpen) 392 return MTP_RESPONSE_SESSION_NOT_OPEN; 393 394 int count = mStorages.size(); 395 mData.putUInt32(count); 396 for (int i = 0; i < count; i++) 397 mData.putUInt32(mStorages[i]->getStorageID()); 398 399 return MTP_RESPONSE_OK; 400} 401 402MtpResponseCode MtpServer::doGetStorageInfo() { 403 MtpStringBuffer string; 404 405 if (!mSessionOpen) 406 return MTP_RESPONSE_SESSION_NOT_OPEN; 407 MtpStorageID id = mRequest.getParameter(1); 408 MtpStorage* storage = getStorage(id); 409 if (!storage) 410 return MTP_RESPONSE_INVALID_STORAGE_ID; 411 412 mData.putUInt16(storage->getType()); 413 mData.putUInt16(storage->getFileSystemType()); 414 mData.putUInt16(storage->getAccessCapability()); 415 mData.putUInt64(storage->getMaxCapacity()); 416 mData.putUInt64(storage->getFreeSpace()); 417 mData.putUInt32(1024*1024*1024); // Free Space in Objects 418 string.set(storage->getDescription()); 419 mData.putString(string); 420 mData.putEmptyString(); // Volume Identifier 421 422 return MTP_RESPONSE_OK; 423} 424 425MtpResponseCode MtpServer::doGetObjectPropsSupported() { 426 if (!mSessionOpen) 427 return MTP_RESPONSE_SESSION_NOT_OPEN; 428 MtpObjectFormat format = mRequest.getParameter(1); 429 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format); 430 mData.putAUInt16(properties); 431 delete properties; 432 return MTP_RESPONSE_OK; 433} 434 435MtpResponseCode MtpServer::doGetObjectHandles() { 436 if (!mSessionOpen) 437 return MTP_RESPONSE_SESSION_NOT_OPEN; 438 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 439 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 440 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 441 // 0x00000000 for all objects? 442 if (parent == 0xFFFFFFFF) 443 parent = 0; 444 445 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 446 mData.putAUInt32(handles); 447 delete handles; 448 return MTP_RESPONSE_OK; 449} 450 451MtpResponseCode MtpServer::doGetNumObjects() { 452 if (!mSessionOpen) 453 return MTP_RESPONSE_SESSION_NOT_OPEN; 454 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 455 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 456 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 457 // 0x00000000 for all objects? 458 if (parent == 0xFFFFFFFF) 459 parent = 0; 460 461 int count = mDatabase->getNumObjects(storageID, format, parent); 462 if (count >= 0) { 463 mResponse.setParameter(1, count); 464 return MTP_RESPONSE_OK; 465 } else { 466 mResponse.setParameter(1, 0); 467 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 468 } 469} 470 471MtpResponseCode MtpServer::doGetObjectReferences() { 472 if (!mSessionOpen) 473 return MTP_RESPONSE_SESSION_NOT_OPEN; 474 MtpStorageID handle = mRequest.getParameter(1); 475 476 // FIXME - check for invalid object handle 477 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle); 478 if (handles) { 479 mData.putAUInt32(handles); 480 delete handles; 481 } else { 482 mData.putEmptyArray(); 483 } 484 return MTP_RESPONSE_OK; 485} 486 487MtpResponseCode MtpServer::doSetObjectReferences() { 488 if (!mSessionOpen) 489 return MTP_RESPONSE_SESSION_NOT_OPEN; 490 MtpStorageID handle = mRequest.getParameter(1); 491 MtpObjectHandleList* references = mData.getAUInt32(); 492 MtpResponseCode result = mDatabase->setObjectReferences(handle, references); 493 delete references; 494 return result; 495} 496 497MtpResponseCode MtpServer::doGetObjectPropValue() { 498 MtpObjectHandle handle = mRequest.getParameter(1); 499 MtpObjectProperty property = mRequest.getParameter(2); 500 LOGV("GetObjectPropValue %d %s\n", handle, 501 MtpDebug::getObjectPropCodeName(property)); 502 503 return mDatabase->getObjectPropertyValue(handle, property, mData); 504} 505 506MtpResponseCode MtpServer::doSetObjectPropValue() { 507 MtpObjectHandle handle = mRequest.getParameter(1); 508 MtpObjectProperty property = mRequest.getParameter(2); 509 LOGV("SetObjectPropValue %d %s\n", handle, 510 MtpDebug::getObjectPropCodeName(property)); 511 512 return mDatabase->setObjectPropertyValue(handle, property, mData); 513} 514 515MtpResponseCode MtpServer::doGetDevicePropValue() { 516 MtpDeviceProperty property = mRequest.getParameter(1); 517 LOGV("GetDevicePropValue %s\n", 518 MtpDebug::getDevicePropCodeName(property)); 519 520 return mDatabase->getDevicePropertyValue(property, mData); 521} 522 523MtpResponseCode MtpServer::doSetDevicePropValue() { 524 MtpDeviceProperty property = mRequest.getParameter(1); 525 LOGV("SetDevicePropValue %s\n", 526 MtpDebug::getDevicePropCodeName(property)); 527 528 return mDatabase->setDevicePropertyValue(property, mData); 529} 530 531MtpResponseCode MtpServer::doResetDevicePropValue() { 532 MtpDeviceProperty property = mRequest.getParameter(1); 533 LOGV("ResetDevicePropValue %s\n", 534 MtpDebug::getDevicePropCodeName(property)); 535 536 return mDatabase->resetDeviceProperty(property); 537} 538 539MtpResponseCode MtpServer::doGetObjectPropList() { 540 541 MtpObjectHandle handle = mRequest.getParameter(1); 542 // use uint32_t so we can support 0xFFFFFFFF 543 uint32_t format = mRequest.getParameter(2); 544 uint32_t property = mRequest.getParameter(3); 545 int groupCode = mRequest.getParameter(4); 546 int depth = mRequest.getParameter(5); 547 LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", 548 handle, MtpDebug::getFormatCodeName(format), 549 MtpDebug::getObjectPropCodeName(property), groupCode, depth); 550 551 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); 552} 553 554MtpResponseCode MtpServer::doGetObjectInfo() { 555 MtpObjectHandle handle = mRequest.getParameter(1); 556 return mDatabase->getObjectInfo(handle, mData); 557} 558 559MtpResponseCode MtpServer::doGetObject() { 560 MtpObjectHandle handle = mRequest.getParameter(1); 561 MtpString pathBuf; 562 int64_t fileLength; 563 MtpObjectFormat format; 564 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 565 if (result != MTP_RESPONSE_OK) 566 return result; 567 568 const char* filePath = (const char *)pathBuf; 569 mtp_file_range mfr; 570 mfr.fd = open(filePath, O_RDONLY); 571 if (mfr.fd < 0) { 572 return MTP_RESPONSE_GENERAL_ERROR; 573 } 574 mfr.offset = 0; 575 mfr.length = fileLength; 576 577 // send data header 578 mData.setOperationCode(mRequest.getOperationCode()); 579 mData.setTransactionID(mRequest.getTransactionID()); 580 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE); 581 582 // then transfer the file 583 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 584 close(mfr.fd); 585 if (ret < 0) { 586 if (errno == ECANCELED) 587 return MTP_RESPONSE_TRANSACTION_CANCELLED; 588 else 589 return MTP_RESPONSE_GENERAL_ERROR; 590 } 591 return MTP_RESPONSE_OK; 592} 593 594MtpResponseCode MtpServer::doGetPartialObject() { 595 MtpObjectHandle handle = mRequest.getParameter(1); 596 uint32_t offset = mRequest.getParameter(2); 597 uint32_t length = mRequest.getParameter(3); 598 MtpString pathBuf; 599 int64_t fileLength; 600 MtpObjectFormat format; 601 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 602 if (result != MTP_RESPONSE_OK) 603 return result; 604 if (offset + length > fileLength) 605 length = fileLength - offset; 606 607 const char* filePath = (const char *)pathBuf; 608 mtp_file_range mfr; 609 mfr.fd = open(filePath, O_RDONLY); 610 if (mfr.fd < 0) { 611 return MTP_RESPONSE_GENERAL_ERROR; 612 } 613 mfr.offset = offset; 614 mfr.length = length; 615 mResponse.setParameter(1, length); 616 617 // send data header 618 mData.setOperationCode(mRequest.getOperationCode()); 619 mData.setTransactionID(mRequest.getTransactionID()); 620 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE); 621 622 // then transfer the file 623 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 624 close(mfr.fd); 625 if (ret < 0) { 626 if (errno == ECANCELED) 627 return MTP_RESPONSE_TRANSACTION_CANCELLED; 628 else 629 return MTP_RESPONSE_GENERAL_ERROR; 630 } 631 return MTP_RESPONSE_OK; 632} 633 634MtpResponseCode MtpServer::doSendObjectInfo() { 635 MtpString path; 636 MtpStorageID storageID = mRequest.getParameter(1); 637 MtpStorage* storage = getStorage(storageID); 638 MtpObjectHandle parent = mRequest.getParameter(2); 639 if (!storage) 640 return MTP_RESPONSE_INVALID_STORAGE_ID; 641 642 // special case the root 643 if (parent == MTP_PARENT_ROOT) { 644 path = storage->getPath(); 645 parent = 0; 646 } else { 647 int64_t length; 648 MtpObjectFormat format; 649 int result = mDatabase->getObjectFilePath(parent, path, length, format); 650 if (result != MTP_RESPONSE_OK) 651 return result; 652 if (format != MTP_FORMAT_ASSOCIATION) 653 return MTP_RESPONSE_INVALID_PARENT_OBJECT; 654 } 655 656 // read only the fields we need 657 mData.getUInt32(); // storage ID 658 MtpObjectFormat format = mData.getUInt16(); 659 mData.getUInt16(); // protection status 660 mSendObjectFileSize = mData.getUInt32(); 661 mData.getUInt16(); // thumb format 662 mData.getUInt32(); // thumb compressed size 663 mData.getUInt32(); // thumb pix width 664 mData.getUInt32(); // thumb pix height 665 mData.getUInt32(); // image pix width 666 mData.getUInt32(); // image pix height 667 mData.getUInt32(); // image bit depth 668 mData.getUInt32(); // parent 669 uint16_t associationType = mData.getUInt16(); 670 uint32_t associationDesc = mData.getUInt32(); // association desc 671 mData.getUInt32(); // sequence number 672 MtpStringBuffer name, created, modified; 673 mData.getString(name); // file name 674 mData.getString(created); // date created 675 mData.getString(modified); // date modified 676 // keywords follow 677 678 LOGV("name: %s format: %04X\n", (const char *)name, format); 679 time_t modifiedTime; 680 if (!parseDateTime(modified, modifiedTime)) 681 modifiedTime = 0; 682 683 if (path[path.size() - 1] != '/') 684 path += "/"; 685 path += (const char *)name; 686 687 // check space first 688 if (mSendObjectFileSize > storage->getFreeSpace()) 689 return MTP_RESPONSE_STORAGE_FULL; 690 691 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 692 format, parent, storageID, mSendObjectFileSize, modifiedTime); 693 if (handle == kInvalidObjectHandle) { 694 return MTP_RESPONSE_GENERAL_ERROR; 695 } 696 697 if (format == MTP_FORMAT_ASSOCIATION) { 698 mode_t mask = umask(0); 699 int ret = mkdir((const char *)path, mDirectoryPermission); 700 umask(mask); 701 if (ret && ret != -EEXIST) 702 return MTP_RESPONSE_GENERAL_ERROR; 703 chown((const char *)path, getuid(), mFileGroup); 704 705 // SendObject does not get sent for directories, so call endSendObject here instead 706 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK); 707 } else { 708 mSendObjectFilePath = path; 709 // save the handle for the SendObject call, which should follow 710 mSendObjectHandle = handle; 711 mSendObjectFormat = format; 712 } 713 714 mResponse.setParameter(1, storageID); 715 mResponse.setParameter(2, parent); 716 mResponse.setParameter(3, handle); 717 718 return MTP_RESPONSE_OK; 719} 720 721MtpResponseCode MtpServer::doSendObject() { 722 MtpResponseCode result = MTP_RESPONSE_OK; 723 mode_t mask; 724 int ret; 725 726 if (mSendObjectHandle == kInvalidObjectHandle) { 727 LOGE("Expected SendObjectInfo before SendObject"); 728 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 729 goto done; 730 } 731 732 // read the header 733 ret = mData.readDataHeader(mFD); 734 // FIXME - check for errors here. 735 736 // reset so we don't attempt to send this back 737 mData.reset(); 738 739 mtp_file_range mfr; 740 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 741 if (mfr.fd < 0) { 742 result = MTP_RESPONSE_GENERAL_ERROR; 743 goto done; 744 } 745 fchown(mfr.fd, getuid(), mFileGroup); 746 // set permissions 747 mask = umask(0); 748 fchmod(mfr.fd, mFilePermission); 749 umask(mask); 750 751 mfr.offset = 0; 752 mfr.length = mSendObjectFileSize; 753 754 LOGV("receiving %s\n", (const char *)mSendObjectFilePath); 755 // transfer the file 756 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 757 close(mfr.fd); 758 759 LOGV("MTP_RECEIVE_FILE returned %d", ret); 760 761 if (ret < 0) { 762 unlink(mSendObjectFilePath); 763 if (errno == ECANCELED) 764 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 765 else 766 result = MTP_RESPONSE_GENERAL_ERROR; 767 } 768 769done: 770 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 771 result == MTP_RESPONSE_OK); 772 mSendObjectHandle = kInvalidObjectHandle; 773 mSendObjectFormat = 0; 774 return result; 775} 776 777static void deleteRecursive(const char* path) { 778 char pathbuf[PATH_MAX]; 779 int pathLength = strlen(path); 780 if (pathLength >= sizeof(pathbuf) - 1) { 781 LOGE("path too long: %s\n", path); 782 } 783 strcpy(pathbuf, path); 784 if (pathbuf[pathLength - 1] != '/') { 785 pathbuf[pathLength++] = '/'; 786 } 787 char* fileSpot = pathbuf + pathLength; 788 int pathRemaining = sizeof(pathbuf) - pathLength - 1; 789 790 DIR* dir = opendir(path); 791 if (!dir) { 792 LOGE("opendir %s failed: %s", path, strerror(errno)); 793 return; 794 } 795 796 struct dirent* entry; 797 while ((entry = readdir(dir))) { 798 const char* name = entry->d_name; 799 800 // ignore "." and ".." 801 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 802 continue; 803 } 804 805 int nameLength = strlen(name); 806 if (nameLength > pathRemaining) { 807 LOGE("path %s/%s too long\n", path, name); 808 continue; 809 } 810 strcpy(fileSpot, name); 811 812 int type = entry->d_type; 813 if (entry->d_type == DT_DIR) { 814 deleteRecursive(pathbuf); 815 rmdir(pathbuf); 816 } else { 817 unlink(pathbuf); 818 } 819 } 820 closedir(dir); 821} 822 823static void deletePath(const char* path) { 824 struct stat statbuf; 825 if (stat(path, &statbuf) == 0) { 826 if (S_ISDIR(statbuf.st_mode)) { 827 deleteRecursive(path); 828 rmdir(path); 829 } else { 830 unlink(path); 831 } 832 } else { 833 LOGE("deletePath stat failed for %s: %s", path, strerror(errno)); 834 } 835} 836 837MtpResponseCode MtpServer::doDeleteObject() { 838 MtpObjectHandle handle = mRequest.getParameter(1); 839 MtpObjectFormat format = mRequest.getParameter(2); 840 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 841 // FIXME - implement deleting objects by format 842 843 MtpString filePath; 844 int64_t fileLength; 845 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format); 846 if (result == MTP_RESPONSE_OK) { 847 LOGV("deleting %s", (const char *)filePath); 848 deletePath((const char *)filePath); 849 return mDatabase->deleteFile(handle); 850 } else { 851 return result; 852 } 853} 854 855MtpResponseCode MtpServer::doGetObjectPropDesc() { 856 MtpObjectProperty propCode = mRequest.getParameter(1); 857 MtpObjectFormat format = mRequest.getParameter(2); 858 LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), 859 MtpDebug::getFormatCodeName(format)); 860 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); 861 if (!property) 862 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 863 property->write(mData); 864 delete property; 865 return MTP_RESPONSE_OK; 866} 867 868MtpResponseCode MtpServer::doGetDevicePropDesc() { 869 MtpDeviceProperty propCode = mRequest.getParameter(1); 870 LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); 871 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); 872 if (!property) 873 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 874 property->write(mData); 875 delete property; 876 return MTP_RESPONSE_OK; 877} 878 879} // namespace android 880