MtpResponseCode MtpServer::doSendObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; MtpResponseCode result = MTP_RESPONSE_OK; mode_t mask; int ret = 0, initialData; if (mSendObjectHandle == kInvalidObjectHandle) { MTPE("Expected SendObjectInfo before SendObject"); result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; goto done; } // read the header, and possibly some data ret = mData.read(mFD); if (ret < MTP_CONTAINER_HEADER_SIZE) { MTPE("MTP_RESPONSE_GENERAL_ERROR\n"); result = MTP_RESPONSE_GENERAL_ERROR; goto done; } initialData = ret - MTP_CONTAINER_HEADER_SIZE; mtp_file_range mfr; mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, 0640); if (mfr.fd < 0) { result = MTP_RESPONSE_GENERAL_ERROR; MTPE("fd error\n"); goto done; } fchown(mfr.fd, getuid(), mFileGroup); // set permissions mask = umask(0); fchmod(mfr.fd, mFilePermission); umask(mask); if (initialData > 0) ret = write(mfr.fd, mData.getData(), initialData); if (mSendObjectFileSize - initialData > 0) { mfr.offset = initialData; if (mSendObjectFileSize == 0xFFFFFFFF) { // tell driver to read until it receives a short packet mfr.length = 0xFFFFFFFF; } else { mfr.length = mSendObjectFileSize - initialData; } MTPD("receiving %s\n", (const char *)mSendObjectFilePath); // transfer the file ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); } close(mfr.fd); tw_set_default_metadata((const char *)mSendObjectFilePath); if (ret < 0) { unlink(mSendObjectFilePath); if (errno == ECANCELED) result = MTP_RESPONSE_TRANSACTION_CANCELLED; else { MTPD("errno: %d\n", errno); result = MTP_RESPONSE_GENERAL_ERROR; } } done: // reset so we don't attempt to send the data back MTPD("MTP_RECEIVE_FILE returned %d\n", ret); mData.reset(); mDatabase->lockMutex(); mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, result == MTP_RESPONSE_OK); mDatabase->unlockMutex(); mSendObjectHandle = kInvalidObjectHandle; MTPD("result: %d\n", result); mSendObjectFormat = 0; return result; }
MtpResponseCode MtpServer::doSendPartialObject() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; MtpObjectHandle handle = mRequest.getParameter(1); uint64_t offset = mRequest.getParameter(2); uint64_t offset2 = mRequest.getParameter(3); offset = offset | (offset2 << 32); uint32_t length = mRequest.getParameter(4); ObjectEdit* edit = getEditObject(handle); if (!edit) { MTPE("object not open for edit in doSendPartialObject"); return MTP_RESPONSE_GENERAL_ERROR; } // can't start writing past the end of the file if (offset > edit->mSize) { MTPE("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize); return MTP_RESPONSE_GENERAL_ERROR; } const char* filePath = (const char *)edit->mPath; MTPD("receiving partial %s %lld %lld\n", filePath, offset, length); // read the header, and possibly some data int ret = mData.read(mFD); if (ret < MTP_CONTAINER_HEADER_SIZE) return MTP_RESPONSE_GENERAL_ERROR; int initialData = ret - MTP_CONTAINER_HEADER_SIZE; if (initialData > 0) { ret = write(edit->mFD, mData.getData(), initialData); offset += initialData; length -= initialData; } if (length > 0) { mtp_file_range mfr; mfr.fd = edit->mFD; mfr.offset = offset; mfr.length = length; // transfer the file ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); MTPD("MTP_RECEIVE_FILE returned %d", ret); } if (ret < 0) { mResponse.setParameter(1, 0); if (errno == ECANCELED) return MTP_RESPONSE_TRANSACTION_CANCELLED; else return MTP_RESPONSE_GENERAL_ERROR; } // reset so we don't attempt to send this back mData.reset(); mResponse.setParameter(1, length); uint64_t end = offset + length; if (end > edit->mSize) { edit->mSize = end; } return MTP_RESPONSE_OK; }
MtpResponseCode MtpServer::doGetDeviceInfo() { MtpStringBuffer string; char prop_value[PROPERTY_VALUE_MAX]; MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats(); MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats(); MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties(); // fill in device info mData.putUInt16(MTP_STANDARD_VERSION); if (mPtp) { MTPD("doGetDeviceInfo putting 0\n"); mData.putUInt32(0); } else { // MTP Vendor Extension ID MTPD("doGetDeviceInfo putting 6\n"); mData.putUInt32(6); } mData.putUInt16(MTP_STANDARD_VERSION); if (mPtp) { // no extensions MTPD("doGetDeviceInfo no extensions\n"); string.set(""); } else { // MTP extensions MTPD("doGetDeviceInfo microsoft.com: 1.0; android.com: 1.0;\n"); string.set("microsoft.com: 1.0; android.com: 1.0;"); } mData.putString(string); // MTP Extensions mData.putUInt16(0); //Functional Mode MTPD("doGetDeviceInfo opcodes, %i\n", sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); MTPD("doGetDeviceInfo eventcodes, %i\n", sizeof(kSupportedEventCodes) / sizeof(uint16_t)); mData.putAUInt16(kSupportedOperationCodes, sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported mData.putAUInt16(kSupportedEventCodes, sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported mData.putAUInt16(deviceProperties); // Device Properties Supported mData.putAUInt16(captureFormats); // Capture Formats mData.putAUInt16(playbackFormats); // Playback Formats property_get("ro.product.manufacturer", prop_value, "unknown manufacturer"); MTPD("prop: %s\n", prop_value); string.set(prop_value); mData.putString(string); // Manufacturer property_get("ro.product.model", prop_value, "MTP Device"); string.set(prop_value); mData.putString(string); // Model string.set("1.0"); mData.putString(string); // Device Version property_get("ro.serialno", prop_value, "????????"); MTPD("sn: %s\n", prop_value); string.set(prop_value); mData.putString(string); // Serial Number delete playbackFormats; delete captureFormats; delete deviceProperties; return MTP_RESPONSE_OK; }
MtpResponseCode MtpServer::doSendObjectInfo() { MTPD("MtpServer::doSendObjectInfo starting\n"); MtpString path; MtpStorageID storageID = mRequest.getParameter(1); MtpStorage* storage = getStorage(storageID); MtpObjectHandle parent = mRequest.getParameter(2); if (!storage) return MTP_RESPONSE_INVALID_STORAGE_ID; // special case the root if (parent == MTP_PARENT_ROOT) { MTPD("MtpServer::doSendObjectInfo special case root\n"); path = storage->getPath(); parent = 0; } else { int64_t length; MtpObjectFormat format; MTPD("MtpServer::doSendObjectInfo calling getObjectFilePath\n"); mDatabase->lockMutex(); int result = mDatabase->getObjectFilePath(parent, path, length, format); mDatabase->unlockMutex(); if (result != MTP_RESPONSE_OK) { return result; } if (format != MTP_FORMAT_ASSOCIATION) return MTP_RESPONSE_INVALID_PARENT_OBJECT; } // read only the fields we need mData.getUInt32(); // storage ID MtpObjectFormat format = mData.getUInt16(); mData.getUInt16(); // protection status mSendObjectFileSize = mData.getUInt32(); mData.getUInt16(); // thumb format mData.getUInt32(); // thumb compressed size mData.getUInt32(); // thumb pix width mData.getUInt32(); // thumb pix height mData.getUInt32(); // image pix width mData.getUInt32(); // image pix height mData.getUInt32(); // image bit depth mData.getUInt32(); // parent uint16_t associationType = mData.getUInt16(); uint32_t associationDesc = mData.getUInt32(); // association desc mData.getUInt32(); // sequence number MtpStringBuffer name, created, modified; mData.getString(name); // file name mData.getString(created); // date created mData.getString(modified); // date modified // keywords follow MTPD("name: %s format: %04X\n", (const char *)name, format); time_t modifiedTime; if (!parseDateTime(modified, modifiedTime)) { modifiedTime = 0; } if (path[path.size() - 1] != '/') { path += "/"; } path += (const char *)name; // check space first if (mSendObjectFileSize > storage->getFreeSpace()) return MTP_RESPONSE_STORAGE_FULL; uint64_t maxFileSize = storage->getMaxFileSize(); // check storage max file size MTPD("maxFileSize: %ld\n", maxFileSize); if (maxFileSize != 0) { // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size // is >= 0xFFFFFFFF if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF) return MTP_RESPONSE_OBJECT_TOO_LARGE; } MTPD("MtpServer::doSendObjectInfo path: %s parent: %d storageID: %08X\n", (const char*)path, parent, storageID); mDatabase->lockMutex(); MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, format, parent, storageID, mSendObjectFileSize, modifiedTime); mDatabase->unlockMutex(); if (handle == kInvalidObjectHandle) { MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, handle == kInvalidObjectHandle\n"); return MTP_RESPONSE_GENERAL_ERROR; } if (format == MTP_FORMAT_ASSOCIATION) { mode_t mask = umask(0); MTPD("MtpServer::doSendObjectInfo mkdir '%s'\n", (const char *)path); int ret = mkdir((const char *)path, mDirectoryPermission); umask(mask); if (ret && ret != -EEXIST) { MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, ret && ret != -EEXIST\n"); return MTP_RESPONSE_GENERAL_ERROR; } chown((const char *)path, getuid(), mFileGroup); tw_set_default_metadata((const char *)path); // SendObject does not get sent for directories, so call endSendObject here instead mDatabase->lockMutex(); mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK); mDatabase->unlockMutex(); } else { mSendObjectFilePath = path; // save the handle for the SendObject call, which should follow mSendObjectHandle = handle; mSendObjectFormat = format; } mResponse.setParameter(1, storageID); mResponse.setParameter(2, parent); mResponse.setParameter(3, handle); MTPD("MtpServer::doSendObjectInfo returning MTP_RESPONSE_OK\n"); return MTP_RESPONSE_OK; }
void MtpServer::sendStoreRemoved(MtpStorageID id) { MTPD("sendStoreRemoved %08X\n", id); sendEvent(MTP_EVENT_STORE_REMOVED, id); MTPD("MtpServer::sendStoreRemoved done\n"); }
bool MtpServer::handleRequest() { android::Mutex::Autolock autoLock(mMutex); MtpOperationCode operation = mRequest.getOperationCode(); MtpResponseCode response; mResponse.reset(); if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { // FIXME - need to delete mSendObjectHandle from the database MTPE("expected SendObject after SendObjectInfo"); mSendObjectHandle = kInvalidObjectHandle; } switch (operation) { case MTP_OPERATION_GET_DEVICE_INFO: MTPD("doGetDeviceInfo()\n"); response = doGetDeviceInfo(); break; case MTP_OPERATION_OPEN_SESSION: MTPD("doOpenSesion()\n"); response = doOpenSession(); break; case MTP_OPERATION_CLOSE_SESSION: MTPD("doCloseSession()\n"); response = doCloseSession(); break; case MTP_OPERATION_GET_STORAGE_IDS: MTPD("doGetStorageIDs()\n"); response = doGetStorageIDs(); break; case MTP_OPERATION_GET_STORAGE_INFO: MTPD("about to call doGetStorageInfo()\n"); response = doGetStorageInfo(); break; case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: MTPD("about to call doGetObjectPropsSupported()\n"); response = doGetObjectPropsSupported(); break; case MTP_OPERATION_GET_OBJECT_HANDLES: MTPD("about to call doGetObjectHandles()\n"); response = doGetObjectHandles(); break; case MTP_OPERATION_GET_NUM_OBJECTS: MTPD("about to call doGetNumbObjects()\n"); response = doGetNumObjects(); break; case MTP_OPERATION_GET_OBJECT_REFERENCES: MTPD("about to call doGetObjectReferences()\n"); response = doGetObjectReferences(); break; case MTP_OPERATION_SET_OBJECT_REFERENCES: MTPD("about to call doSetObjectReferences()\n"); response = doSetObjectReferences(); break; case MTP_OPERATION_GET_OBJECT_PROP_VALUE: MTPD("about to call doGetObjectPropValue()\n"); response = doGetObjectPropValue(); break; case MTP_OPERATION_SET_OBJECT_PROP_VALUE: MTPD("about to call doSetObjectPropValue()\n"); response = doSetObjectPropValue(); break; case MTP_OPERATION_GET_DEVICE_PROP_VALUE: MTPD("about to call doGetDevicPropValue()\n"); response = doGetDevicePropValue(); break; case MTP_OPERATION_SET_DEVICE_PROP_VALUE: MTPD("about to call doSetDevicePropVaue()\n"); response = doSetDevicePropValue(); break; case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: MTPD("about to call doResetDevicePropValue()\n"); response = doResetDevicePropValue(); break; case MTP_OPERATION_GET_OBJECT_PROP_LIST: MTPD("calling doGetObjectPropList()\n"); response = doGetObjectPropList(); break; case MTP_OPERATION_GET_OBJECT_INFO: MTPD("calling doGetObjectInfo()\n"); response = doGetObjectInfo(); break; case MTP_OPERATION_GET_OBJECT: MTPD("about to call doGetObject()\n"); response = doGetObject(); break; case MTP_OPERATION_GET_THUMB: response = doGetThumb(); break; case MTP_OPERATION_GET_PARTIAL_OBJECT: case MTP_OPERATION_GET_PARTIAL_OBJECT_64: response = doGetPartialObject(operation); break; case MTP_OPERATION_SEND_OBJECT_INFO: MTPD("about to call doSendObjectInfo()\n"); response = doSendObjectInfo(); break; case MTP_OPERATION_SEND_OBJECT: MTPD("about to call doSendObject()\n"); response = doSendObject(); break; case MTP_OPERATION_DELETE_OBJECT: response = doDeleteObject(); break; case MTP_OPERATION_GET_OBJECT_PROP_DESC: MTPD("about to call doGetObjectPropDesc()\n"); response = doGetObjectPropDesc(); break; case MTP_OPERATION_GET_DEVICE_PROP_DESC: MTPD("about to call doGetDevicePropDesc()\n"); response = doGetDevicePropDesc(); break; case MTP_OPERATION_SEND_PARTIAL_OBJECT: response = doSendPartialObject(); break; case MTP_OPERATION_TRUNCATE_OBJECT: response = doTruncateObject(); break; case MTP_OPERATION_BEGIN_EDIT_OBJECT: response = doBeginEditObject(); break; case MTP_OPERATION_END_EDIT_OBJECT: response = doEndEditObject(); break; default: MTPE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; break; } if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) return false; mResponse.setResponseCode(response); return true; }
void MtpServer::sendObjectUpdated(MtpObjectHandle handle) { MTPD("sendObjectUpdated %d\n", handle); sendEvent(MTP_EVENT_OBJECT_PROP_CHANGED, handle); }
void MtpServer::sendStoreAdded(MtpStorageID id) { MTPD("sendStoreAdded %08X\n", id); sendEvent(MTP_EVENT_STORE_ADDED, id); }
void MtpServer::sendObjectAdded(MtpObjectHandle handle) { MTPD("sendObjectAdded %d\n", handle); sendEvent(MTP_EVENT_OBJECT_ADDED, handle); }
void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { MTPD("sendObjectRemoved %d\n", handle); sendEvent(MTP_EVENT_OBJECT_REMOVED, handle); }
void MtpServer::run(int fd) { if (fd < 0) return; mFD = fd; MTPI("MtpServer::run fd: %d\n", fd); while (1) { MTPD("About to read device...\n"); int ret = mRequest.read(fd); if (ret < 0) { if (errno == ECANCELED) { // return to top of loop and wait for next command MTPD("request read returned %d ECANCELED, starting over\n", ret); continue; } MTPE("request read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno); break; } MtpOperationCode operation = mRequest.getOperationCode(); MtpTransactionID transaction = mRequest.getTransactionID(); MTPD("operation: %s", MtpDebug::getOperationCodeName(operation)); mRequest.dump(); // FIXME need to generalize this bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO || operation == MTP_OPERATION_SET_OBJECT_REFERENCES || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE); if (dataIn) { int ret = mData.read(fd); if (ret < 0) { if (errno == ECANCELED) { // return to top of loop and wait for next command MTPD("data read returned %d ECANCELED, starting over\n", ret); continue; } MTPD("data read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno); break; } MTPD("received data:"); mData.dump(); } else { mData.reset(); } if (handleRequest()) { if (!dataIn && mData.hasData()) { mData.setOperationCode(operation); mData.setTransactionID(transaction); MTPD("sending data:"); mData.dump(); ret = mData.write(fd); if (ret < 0) { if (errno == ECANCELED) { // return to top of loop and wait for next command MTPD("data write returned %d ECANCELED, starting over\n", ret); continue; } MTPE("data write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno); break; } } mResponse.setTransactionID(transaction); MTPD("sending response %04X\n", mResponse.getResponseCode()); ret = mResponse.write(fd); MTPD("ret: %d\n", ret); mResponse.dump(); if (ret < 0) { if (errno == ECANCELED) { // return to top of loop and wait for next command MTPD("response write returned %d ECANCELED, starting over\n", ret); continue; } MTPE("response write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno); break; } } else { MTPD("skipping response\n"); } } // commit any open edits int count = mObjectEditList.size(); for (int i = 0; i < count; i++) { ObjectEdit* edit = mObjectEditList[i]; commitEdit(edit); delete edit; } mObjectEditList.clear(); if (mSessionOpen) mDatabase->sessionEnded(); // This doesn't actually do anything but was carry over from AOSP close(fd); mFD = -1; }
bool MtpServer::hasStorage(MtpStorageID id) { MTPD("in hasStorage\n"); if (id == 0 || id == 0xFFFFFFFF) return mStorages.size() > 0; return (getStorage(id) != NULL); }
void MtpStorage::handleInotifyEvent(struct inotify_event* event) { std::map<int, Tree*>::iterator it = inotifymap.find(event->wd); if (it == inotifymap.end()) { MTPE("Unable to locate inotify_wd: %i\n", event->wd); return; } Tree* tree = it->second; MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str()); Node* node = tree->findEntryByName(basename(event->name)); if (node && node->Mtpid() == handleCurrentlySending) { MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid()); return; } if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) { if (event->mask & IN_ISDIR) { MTPD("inotify_t create is dir\n"); } else { MTPD("inotify_t create is file\n"); } if (node == NULL) { node = addNewNode(event->mask & IN_ISDIR, tree, event->name); std::string item = getNodePath(tree) + "/" + event->name; node->addProperties(item, getStorageID()); mServer->sendObjectAdded(node->Mtpid()); } else { MTPD("inotify_t item already exists.\n"); } if (event->mask & IN_ISDIR) { // TODO: do we need to do anything here? probably not until someone reads from the dir... } } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) { if (event->mask & IN_ISDIR) { MTPD("inotify_t Directory %s deleted\n", event->name); } else { MTPD("inotify_t File %s deleted\n", event->name); } if (node) { if (event->mask & IN_ISDIR) { for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) { if (it->second == node) { inotify_rm_watch(inotify_fd, it->first); MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str()); inotifymap.erase(it->first); break; } } } MtpObjectHandle handle = node->Mtpid(); deleteFile(handle); mServer->sendObjectRemoved(handle); } else { MTPD("inotify_t already removed.\n"); } } else if (event->mask & IN_MODIFY) { MTPD("inotify_t item %s modified.\n", event->name); if (node != NULL) { uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt; struct stat st; uint64_t new_size = 0; if (lstat(getNodePath(node).c_str(), &st) == 0) new_size = (uint64_t)st.st_size; if (orig_size != new_size) { MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid()); node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64); mServer->sendObjectUpdated(node->Mtpid()); } } else { MTPE("inotify_t modified item not found\n"); } } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) { // TODO: is this always already handled by IN_DELETE for the parent dir? } }
int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) { MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property); if (groupCode != 0) { MTPE("getObjectPropertyList: groupCode unsupported\n"); return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED } // TODO: support all the special stuff, like: // handle == 0 -> all objects at the root level // handle == 0xffffffff -> all objects (on all storages? how could we support that?) // format == 0 -> all formats, otherwise filter by ObjectFormatCode // property == 0xffffffff -> all properties except those with group code 0xffffffff // if property == 0 then use groupCode // groupCode == 0 -> return Specification_By_Group_Unsupported // depth == 0xffffffff -> all objects incl. and below handle std::vector<PropEntry> results; if (handle == 0xffffffff) { // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage) } else if (handle == 0) { // all objects at the root level Tree* root = mtpmap[0]; MtpObjectHandleList list; root->getmtpids(&list); for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) { Node* node = root->findNode(*it); if (!node) { MTPE("BUG: node not found for root entry with handle %u\n", *it); break; } queryNodeProperties(results, node, property, groupCode, mStorageID); } } else { // single object Node* node = findNode(handle); if (!node) { // Item is not on this storage device return -1; } queryNodeProperties(results, node, property, groupCode, mStorageID); } MTPD("count: %u\n", results.size()); packet.putUInt32(results.size()); for (size_t i = 0; i < results.size(); ++i) { PropEntry& p = results[i]; MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n", p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property), p.datatype, p.intvalue); packet.putUInt32(p.handle); packet.putUInt16(p.property); packet.putUInt16(p.datatype); switch (p.datatype) { case MTP_TYPE_INT8: MTPD("MTP_TYPE_INT8\n"); packet.putInt8(p.intvalue); break; case MTP_TYPE_UINT8: MTPD("MTP_TYPE_UINT8\n"); packet.putUInt8(p.intvalue); break; case MTP_TYPE_INT16: MTPD("MTP_TYPE_INT16\n"); packet.putInt16(p.intvalue); break; case MTP_TYPE_UINT16: MTPD("MTP_TYPE_UINT16\n"); packet.putUInt16(p.intvalue); break; case MTP_TYPE_INT32: MTPD("MTP_TYPE_INT32\n"); packet.putInt32(p.intvalue); break; case MTP_TYPE_UINT32: MTPD("MTP_TYPE_UINT32\n"); packet.putUInt32(p.intvalue); break; case MTP_TYPE_INT64: MTPD("MTP_TYPE_INT64\n"); packet.putInt64(p.intvalue); break; case MTP_TYPE_UINT64: MTPD("MTP_TYPE_UINT64\n"); packet.putUInt64(p.intvalue); break; case MTP_TYPE_INT128: MTPD("MTP_TYPE_INT128\n"); packet.putInt128(p.intvalue); break; case MTP_TYPE_UINT128: MTPD("MTP_TYPE_UINT128\n"); packet.putUInt128(p.intvalue); break; case MTP_TYPE_STR: MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str()); packet.putString(p.strvalue.c_str()); break; default: MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype); break; } } return 0; }
void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID) { MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str()); PropEntry pe; pe.handle = node->Mtpid(); pe.property = property; if (property == 0xffffffff) { // add all properties MTPD("MtpStorage::queryNodeProperties for all properties\n"); std::vector<Node::mtpProperty> mtpprop = node->getMtpProps(); for (size_t i = 0; i < mtpprop.size(); ++i) { pe.property = mtpprop[i].property; pe.datatype = mtpprop[i].dataType; pe.intvalue = mtpprop[i].valueInt; pe.strvalue = mtpprop[i].valueStr; results.push_back(pe); } return; } else if (property == 0) { // TODO: use groupCode } // single property // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue switch (property) { // case MTP_PROPERTY_OBJECT_FORMAT: // pe.datatype = MTP_TYPE_UINT16; // pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT); // break; case MTP_PROPERTY_STORAGE_ID: pe.datatype = MTP_TYPE_UINT32; pe.intvalue = storageID; break; case MTP_PROPERTY_PROTECTION_STATUS: pe.datatype = MTP_TYPE_UINT16; pe.intvalue = 0; break; case MTP_PROPERTY_OBJECT_SIZE: { pe.datatype = MTP_TYPE_UINT64; struct stat st; pe.intvalue = 0; if (lstat(getNodePath(node).c_str(), &st) == 0) pe.intvalue = st.st_size; break; } default: { const Node::mtpProperty& prop = node->getProperty(property); if (prop.property != property) { MTPD("queryNodeProperties: unknown property %x\n", property); return; } pe.datatype = prop.dataType; pe.intvalue = prop.valueInt; pe.strvalue = prop.valueStr; // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here } } results.push_back(pe); }