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