Commit 53735bef authored by Bandan Das's avatar Bandan Das Committed by Gerd Hoffmann
Browse files

usb-mtp: Advertise SendObjectInfo for write support



This patch implements a dummy ObjectInfo structure so that
it's easy to typecast the incoming data. If the metadata is
valid, write_pending is set. Also, the incoming filename
is utf-16, so, instead of depending on external libraries, just
implement a simple function to get the filename

Signed-off-by: default avatarBandan Das <bsd@redhat.com>
Message-id: 20180223164829.29683-6-bsd@redhat.com
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 88d5f381
Loading
Loading
Loading
Loading
+134 −2
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ enum mtp_code {
    CMD_GET_OBJECT_INFO            = 0x1008,
    CMD_GET_OBJECT                 = 0x1009,
    CMD_DELETE_OBJECT              = 0x100b,
    CMD_SEND_OBJECT_INFO           = 0x100c,
    CMD_SEND_OBJECT                = 0x100d,
    CMD_GET_PARTIAL_OBJECT         = 0x101b,
    CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801,
@@ -67,8 +68,10 @@ enum mtp_code {
    RES_STORE_FULL                 = 0x200c,
    RES_STORE_READ_ONLY            = 0x200e,
    RES_PARTIAL_DELETE             = 0x2012,
    RES_STORE_NOT_AVAILABLE        = 0x2013,
    RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014,
    RES_INVALID_OBJECTINFO         = 0x2015,
    RES_DESTINATION_UNSUPPORTED    = 0x2020,
    RES_INVALID_PARENT_OBJECT      = 0x201a,
    RES_INVALID_PARAMETER          = 0x201d,
    RES_SESSION_ALREADY_OPEN       = 0x201e,
@@ -196,6 +199,34 @@ struct MTPState {
    } dataset;
};

/*
 * ObjectInfo dataset received from initiator
 * Fields we don't care about are ignored
 */
typedef struct {
    uint32_t storage_id; /*unused*/
    uint16_t format;
    uint16_t protection_status; /*unused*/
    uint32_t size;
    uint16_t thumb_format; /*unused*/
    uint32_t thumb_comp_sz; /*unused*/
    uint32_t thumb_pix_width; /*unused*/
    uint32_t thumb_pix_height; /*unused*/
    uint32_t image_pix_width; /*unused*/
    uint32_t image_pix_height; /*unused*/
    uint32_t image_bit_depth; /*unused*/
    uint32_t parent; /*unused*/
    uint16_t assoc_type;
    uint32_t assoc_desc;
    uint32_t seq_no; /*unused*/
    uint8_t length; /*part of filename field*/
    uint16_t filename[0];
    char date_created[0]; /*unused*/
    char date_modified[0]; /*unused*/
    char keywords[0]; /*unused*/
    /* string and other data follows */
} QEMU_PACKED ObjectInfo;

#define TYPE_USB_MTP "usb-mtp"
#define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP)

@@ -437,7 +468,6 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
    return child;
}

#ifdef CONFIG_INOTIFY1
static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
                                             char *name, int len)
{
@@ -452,6 +482,7 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
    return NULL;
}

#ifdef CONFIG_INOTIFY1
static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
{
    MTPObject *iter;
@@ -815,6 +846,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_INFO,
        CMD_SEND_OBJECT,
        CMD_GET_OBJECT,
        CMD_GET_PARTIAL_OBJECT,
@@ -1243,7 +1275,7 @@ static void usb_mtp_object_delete(MTPState *s, uint32_t handle,
static void usb_mtp_command(MTPState *s, MTPControl *c)
{
    MTPData *data_in = NULL;
    MTPObject *o;
    MTPObject *o = NULL;
    uint32_t nres = 0, res0 = 0;

    /* sanity checks */
@@ -1390,6 +1422,41 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
        nres = 1;
        res0 = data_in->length;
        break;
    case CMD_SEND_OBJECT_INFO:
        /* Return error if store is read-only */
        if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) {
            usb_mtp_queue_result(s, RES_STORE_READ_ONLY,
                                 c->trans, 0, 0, 0, 0);
        } else if (c->argv[0] && (c->argv[0] != QEMU_STORAGE_ID)) {
            /* First parameter points to storage id or is 0 */
            usb_mtp_queue_result(s, RES_STORE_NOT_AVAILABLE, c->trans,
                                 0, 0, 0, 0);
        } else if (c->argv[1] && !c->argv[0]) {
            /* If second parameter is specified, first must also be specified */
            usb_mtp_queue_result(s, RES_DESTINATION_UNSUPPORTED, c->trans,
                                 0, 0, 0, 0);
        } else {
            uint32_t handle = c->argv[1];
            if (handle == 0xFFFFFFFF || handle == 0) {
                /* root object */
                o = QTAILQ_FIRST(&s->objects);
            } else {
                o = usb_mtp_object_lookup(s, handle);
            }
            if (o == NULL) {
                usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans,
                                     0, 0, 0, 0);
            }
            if (o->format != FMT_ASSOCIATION) {
                usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans,
                                     0, 0, 0, 0);
            }
        }
        if (o) {
            s->dataset.parent_handle = o->handle;
        }
        s->data_out = usb_mtp_data_alloc(c);
        return;
    case CMD_SEND_OBJECT:
        if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) {
            usb_mtp_queue_result(s, RES_STORE_READ_ONLY,
@@ -1497,6 +1564,19 @@ static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p)
    fprintf(stderr, "%s\n", __func__);
}

static void utf16_to_str(uint8_t len, uint16_t *arr, char *name)
{
    int count;
    wchar_t *wstr = g_new0(wchar_t, len);

    for (count = 0; count < len; count++) {
        wstr[count] = (wchar_t)arr[count];
    }

    wcstombs(name, wstr, len);
    g_free(wstr);
}

static void usb_mtp_write_data(MTPState *s)
{
    MTPData *d = s->data_out;
@@ -1570,6 +1650,45 @@ free:
    s->write_pending = false;
}

static void usb_mtp_write_metadata(MTPState *s)
{
    MTPData *d = s->data_out;
    ObjectInfo *dataset = (ObjectInfo *)d->data;
    char *filename = g_new0(char, dataset->length);
    MTPObject *o;
    MTPObject *p = usb_mtp_object_lookup(s, s->dataset.parent_handle);
    uint32_t next_handle = s->next_handle;

    assert(!s->write_pending);

    utf16_to_str(dataset->length, dataset->filename, filename);

    o = usb_mtp_object_lookup_name(p, filename, dataset->length);
    if (o != NULL) {
        next_handle = o->handle;
    }

    s->dataset.filename = filename;
    s->dataset.format = dataset->format;
    s->dataset.size = dataset->size;
    s->dataset.filename = filename;
    s->write_pending = true;

    if (s->dataset.format == FMT_ASSOCIATION) {
        usb_mtp_write_data(s);
        /* next_handle will be allocated to the newly created dir */
        if (d->fd == -1) {
            usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
                                 0, 0, 0, 0);
            return;
        }
        d->fd = -1;
    }

    usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID,
                         s->dataset.parent_handle, next_handle);
}

static void usb_mtp_get_data(MTPState *s, mtp_container *container,
                             USBPacket *p)
{
@@ -1594,6 +1713,19 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container,
    }

    switch (d->code) {
    case CMD_SEND_OBJECT_INFO:
        usb_packet_copy(p, d->data + d->offset, dlen);
        d->offset += dlen;
        if (d->offset == d->length) {
            /* The operation might have already failed */
            if (!s->result) {
                usb_mtp_write_metadata(s);
            }
            usb_mtp_data_free(s->data_out);
            s->data_out = NULL;
            return;
        }
        break;
    case CMD_SEND_OBJECT:
        usb_packet_copy(p, d->data + d->offset, dlen);
        d->offset += dlen;