Allow write operations on behalf of the initiator. The
precursor to write is the sending of the write metadata
that consists of the ObjectInfo dataset. This patch introduces
a flag that is set when the responder is ready to receive
write data based on a previous SendObjectInfo operation by
the initiator (The SendObjectInfo implementation is in a
later patch)
Signed-off-by: Bandan Das
---
hw/usb/dev-mtp.c | 159 ++-
1 file changed, 157 insertions(+), 2 deletions(-)
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 5f53f200c4..8d615cabc0 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -47,6 +47,7 @@ enum mtp_code {
CMD_GET_OBJECT_INFO= 0x1008,
CMD_GET_OBJECT = 0x1009,
CMD_DELETE_OBJECT = 0x100b,
+CMD_SEND_OBJECT= 0x100d,
CMD_GET_PARTIAL_OBJECT = 0x101b,
CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801,
CMD_GET_OBJECT_PROP_DESC = 0x9802,
@@ -66,7 +67,9 @@ enum mtp_code {
RES_STORE_READ_ONLY= 0x200e,
RES_PARTIAL_DELETE = 0x2012,
RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014,
+RES_INVALID_OBJECTINFO = 0x2015,
RES_INVALID_PARENT_OBJECT = 0x201a,
+RES_STORE_FULL = 0x200c,
RES_INVALID_PARAMETER = 0x201d,
RES_SESSION_ALREADY_OPEN = 0x201e,
RES_INVALID_OBJECT_PROP_CODE = 0xA801,
@@ -182,6 +185,14 @@ struct MTPState {
int inotifyfd;
QTAILQ_HEAD(events, MTPMonEntry) events;
#endif
+/* Responder is expecting a write operation */
+bool write_pending;
+struct {
+uint32_t parent_handle;
+uint16_t format;
+uint32_t size;
+char *filename;
+} dataset;
};
#define TYPE_USB_MTP "usb-mtp"
@@ -803,6 +814,7 @@ static MTPData *usb_mtp_get_device_info(MTPState *s,
MTPControl *c)
CMD_GET_OBJECT_HANDLES,
CMD_GET_OBJECT_INFO,
CMD_DELETE_OBJECT,
+CMD_SEND_OBJECT,
CMD_GET_OBJECT,
CMD_GET_PARTIAL_OBJECT,
CMD_GET_OBJECT_PROPS_SUPPORTED,
@@ -1381,6 +1393,14 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
nres = 1;
res0 = data_in->length;
break;
+case CMD_SEND_OBJECT:
+if (!s->write_pending) {
+usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO,
+ c->trans, 0, 0, 0, 0);
+return;
+}
+s->data_out = usb_mtp_data_alloc(c);
+return;
case CMD_GET_OBJECT_PROPS_SUPPORTED:
if (c->argv[0] != FMT_UNDEFINED_OBJECT &&
c->argv[0] != FMT_ASSOCIATION) {
@@ -1475,12 +1495,133 @@ static void usb_mtp_cancel_packet(USBDevice *dev,
USBPacket *p)
fprintf(stderr, "%s\n", __func__);
}
+mode_t getumask(void)
+{
+mode_t mask = umask(0);
+umask(mask);
+return mask;
+}
+
+static void usb_mtp_write_data(MTPState *s)
+{
+MTPData *d = s->data_out;
+MTPObject *parent =
+usb_mtp_object_lookup(s, s->dataset.parent_handle);
+char *path = NULL;
+int rc = -1;
+mode_t mask = ~getumask() & 0666;
+
+assert(d != NULL);
+
+if (parent == NULL || !s->write_pending) {
+usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans,
+ 0, 0, 0, 0);
+return;
+}
+
+if (s->dataset.filename) {
+path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename);
+if (s->dataset.format == FMT_ASSOCIATION) {
+d->fd = mkdir(path, mask);
+goto free;
+}
+if (s->dataset.size < d->length) {
+usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
+ 0, 0, 0, 0);
+goto done;
+}
+d->fd = open(path, O_CREAT | O_WRONLY, mask);
+if (d->fd == -1) {
+usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
+ 0, 0, 0, 0);
+goto done;
+}
+
+/*
+ * Return success if initiator sent 0 sized data
+ */
+if (!s->dataset.size) {
+goto success;
+}
+
+rc = write(d->fd, d->data, s->dataset.size);
+if (rc == -1) {
+usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
+ 0, 0, 0, 0);
+goto done;
+}
+if (rc != s->dataset.size) {
+usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans,
+ 0, 0, 0, 0);
+goto done;
+}
+}
+
+success:
+usb_mtp_queue_result(s, RES_OK, d->trans,
+ 0, 0, 0, 0);
+
+done:
+/*
+ * The write dataset is kept around and freed only
+ * on success or if another write request comes in
+ */
+if (d->fd != -1) {
+close(d->fd);
+}
+free:
+g_free(s->dataset.filename);
+g_free(path);
+s->write_pending