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);
}
Ejemplo n.º 13
0
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?
	}
}
Ejemplo n.º 14
0
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;
}
Ejemplo n.º 15
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);
}