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