Re: [Qemu-devel] [PATCH v4 4/5] usb-mtp: Introduce write support for MTP objects

2018-02-21 Thread Bandan Das
Daniel P. Berrangé  writes:

>>  #define TYPE_USB_MTP "usb-mtp"
>> @@ -804,6 +815,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,
>
> Seems we should not advertize this for readonly devices.

Advertising CMD_SEND_OBJECT shows that it's supported but the device is
read-only probably due to a server side setting. It differentiates between
Operation_Not_Supported and Store_Read_Only.

I agree, we should not rely on the initiator to check for this.
I will add a check for readonly when accepting DELETE, OBJECT_INFO
and return the READ_ONLY response code.

Bandan


>>  CMD_GET_OBJECT,
>>  CMD_GET_PARTIAL_OBJECT,
>>  CMD_GET_OBJECT_PROPS_SUPPORTED,
>> @@ -1378,6 +1390,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) {
>> @@ -1472,12 +1492,126 @@ static void usb_mtp_cancel_packet(USBDevice *dev, 
>> USBPacket *p)
>>  fprintf(stderr, "%s\n", __func__);
>>  }
>>  
>> +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 = 0644;
>> +
>> +assert(d != NULL);
>> +
>
>
> Somewhere in here should surely be validating the "readonly" flag.
>
>> +if (parent == NULL || !s->write_pending) {
>> +usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans,
>> + 0, 0, 0, 0);
>> +return;
>> +}
>> +
>
> Regards,
> Daniel



Re: [Qemu-devel] [PATCH v4 4/5] usb-mtp: Introduce write support for MTP objects

2018-02-21 Thread Daniel P . Berrangé
On Wed, Feb 21, 2018 at 12:11:00PM +0100, Gerd Hoffmann wrote:
> > > +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 = 0644;
> > > +
> > > +assert(d != NULL);
> > > +
> > 
> > 
> > Somewhere in here should surely be validating the "readonly" flag.
> > 
> > > +if (parent == NULL || !s->write_pending) {
> 
> Does happens here.  With a readonly device write_pending should
> never be true.

Unless I'm mis-understanding the flow, the next patch appears to set
write_pending = true, in response to a guest command, without checking
the readonly flag.

> 
> > > +usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans,
> > > + 0, 0, 0, 0);
> > > +return;
> > > +}
> 
> But adding an "assert(!readonly)" here as double-check surely doesn't hurt.
> 
> cheers,
>   Gerd
> 

Regards,
Daniel
-- 
|: https://berrange.com  -o-https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o-https://fstop138.berrange.com :|
|: https://entangle-photo.org-o-https://www.instagram.com/dberrange :|



Re: [Qemu-devel] [PATCH v4 4/5] usb-mtp: Introduce write support for MTP objects

2018-02-21 Thread Gerd Hoffmann
> > +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 = 0644;
> > +
> > +assert(d != NULL);
> > +
> 
> 
> Somewhere in here should surely be validating the "readonly" flag.
> 
> > +if (parent == NULL || !s->write_pending) {

Does happens here.  With a readonly device write_pending should
never be true.

> > +usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans,
> > + 0, 0, 0, 0);
> > +return;
> > +}

But adding an "assert(!readonly)" here as double-check surely doesn't hurt.

cheers,
  Gerd




Re: [Qemu-devel] [PATCH v4 4/5] usb-mtp: Introduce write support for MTP objects

2018-02-21 Thread Daniel P . Berrangé
On Tue, Feb 20, 2018 at 05:59:03PM -0500, Bandan Das wrote:
> 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 | 152 
> ++-
>  1 file changed, 150 insertions(+), 2 deletions(-)
> 
> diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
> index 5ef77f3e9f..9b51708614 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,
> @@ -63,9 +64,11 @@ enum mtp_code {
>  RES_INVALID_STORAGE_ID = 0x2008,
>  RES_INVALID_OBJECT_HANDLE  = 0x2009,
>  RES_INVALID_OBJECT_FORMAT_CODE = 0x200b,
> +RES_STORE_FULL = 0x200c,
>  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_INVALID_PARAMETER  = 0x201d,
>  RES_SESSION_ALREADY_OPEN   = 0x201e,
> @@ -183,6 +186,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"
> @@ -804,6 +815,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,

Seems we should not advertize this for readonly devices.

>  CMD_GET_OBJECT,
>  CMD_GET_PARTIAL_OBJECT,
>  CMD_GET_OBJECT_PROPS_SUPPORTED,
> @@ -1378,6 +1390,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) {
> @@ -1472,12 +1492,126 @@ static void usb_mtp_cancel_packet(USBDevice *dev, 
> USBPacket *p)
>  fprintf(stderr, "%s\n", __func__);
>  }
>  
> +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 = 0644;
> +
> +assert(d != NULL);
> +


Somewhere in here should surely be validating the "readonly" flag.

> +if (parent == NULL || !s->write_pending) {
> +usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans,
> + 0, 0, 0, 0);
> +return;
> +}
> +

Regards,
Daniel
-- 
|: https://berrange.com  -o-https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o-https://fstop138.berrange.com :|
|: https://entangle-photo.org-o-https://www.instagram.com/dberrange :|



[Qemu-devel] [PATCH v4 4/5] usb-mtp: Introduce write support for MTP objects

2018-02-20 Thread Bandan Das
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 | 152 ++-
 1 file changed, 150 insertions(+), 2 deletions(-)

diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 5ef77f3e9f..9b51708614 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,
@@ -63,9 +64,11 @@ enum mtp_code {
 RES_INVALID_STORAGE_ID = 0x2008,
 RES_INVALID_OBJECT_HANDLE  = 0x2009,
 RES_INVALID_OBJECT_FORMAT_CODE = 0x200b,
+RES_STORE_FULL = 0x200c,
 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_INVALID_PARAMETER  = 0x201d,
 RES_SESSION_ALREADY_OPEN   = 0x201e,
@@ -183,6 +186,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"
@@ -804,6 +815,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,
@@ -1378,6 +1390,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) {
@@ -1472,12 +1492,126 @@ static void usb_mtp_cancel_packet(USBDevice *dev, 
USBPacket *p)
 fprintf(stderr, "%s\n", __func__);
 }
 
+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 = 0644;
+
+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 = false;
+}
+
+s