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