diff options
author | Bandan Das <bsd@redhat.com> | 2018-02-23 11:48:27 -0500 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2018-02-26 12:18:36 +0100 |
commit | ec6206a68f700bd22aec3e3b98877d45b22d5143 (patch) | |
tree | e0d1cb622a43f774d276e8097c6d0bd0db4357ad /hw/usb | |
parent | 5d13ebeacc22eb854e5d70b18fda1af22a46dc27 (diff) | |
download | qemu-ec6206a68f700bd22aec3e3b98877d45b22d5143.zip |
usb-mtp: Support delete of mtp objects
Write of existing objects by the initiator is acheived by
making a temporary buffer with the new changes, deleting the
old file and then writing a new file with the same name.
Also, add a "readonly" property which needs to be set to false
for deletion to work.
Signed-off-by: Bandan Das <bsd@redhat.com>
Message-id: 20180223164829.29683-4-bsd@redhat.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb')
-rw-r--r-- | hw/usb/dev-mtp.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 63f8f3b90b..5ef77f3e9f 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -46,6 +46,7 @@ enum mtp_code { CMD_GET_OBJECT_HANDLES = 0x1007, CMD_GET_OBJECT_INFO = 0x1008, CMD_GET_OBJECT = 0x1009, + CMD_DELETE_OBJECT = 0x100b, CMD_GET_PARTIAL_OBJECT = 0x101b, CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801, CMD_GET_OBJECT_PROP_DESC = 0x9802, @@ -62,6 +63,8 @@ enum mtp_code { RES_INVALID_STORAGE_ID = 0x2008, RES_INVALID_OBJECT_HANDLE = 0x2009, RES_INVALID_OBJECT_FORMAT_CODE = 0x200b, + RES_STORE_READ_ONLY = 0x200e, + RES_PARTIAL_DELETE = 0x2012, RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, RES_INVALID_PARENT_OBJECT = 0x201a, RES_INVALID_PARAMETER = 0x201d, @@ -172,6 +175,7 @@ struct MTPState { MTPControl *result; uint32_t session; uint32_t next_handle; + bool readonly; QTAILQ_HEAD(, MTPObject) objects; #ifdef CONFIG_INOTIFY1 @@ -799,6 +803,7 @@ static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) CMD_GET_NUM_OBJECTS, CMD_GET_OBJECT_HANDLES, CMD_GET_OBJECT_INFO, + CMD_DELETE_OBJECT, CMD_GET_OBJECT, CMD_GET_PARTIAL_OBJECT, CMD_GET_OBJECT_PROPS_SUPPORTED, @@ -1113,6 +1118,116 @@ static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c, return d; } +/* Return correct return code for a delete event */ +enum { + ALL_DELETE, + PARTIAL_DELETE, + READ_ONLY, +}; + +/* Assumes that children, if any, have been already freed */ +static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) +{ +#ifndef CONFIG_INOTIFY1 + assert(o->nchildren == 0); + QTAILQ_REMOVE(&s->objects, o, next); + g_free(o->name); + g_free(o->path); + g_free(o); +#endif +} + +static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) +{ + MTPObject *iter, *iter2; + bool partial_delete = false; + bool success = false; + + /* + * TODO: Add support for Protection Status + */ + + QLIST_FOREACH(iter, &o->children, list) { + if (iter->format == FMT_ASSOCIATION) { + QLIST_FOREACH(iter2, &iter->children, list) { + usb_mtp_deletefn(s, iter2, trans); + } + } + } + + if (o->format == FMT_UNDEFINED_OBJECT) { + if (remove(o->path)) { + partial_delete = true; + } else { + usb_mtp_object_free_one(s, o); + success = true; + } + } + + if (o->format == FMT_ASSOCIATION) { + if (rmdir(o->path)) { + partial_delete = true; + } else { + usb_mtp_object_free_one(s, o); + success = true; + } + } + + if (success && partial_delete) { + return PARTIAL_DELETE; + } + if (!success && partial_delete) { + return READ_ONLY; + } + return ALL_DELETE; +} + +static void usb_mtp_object_delete(MTPState *s, uint32_t handle, + uint32_t format_code, uint32_t trans) +{ + MTPObject *o; + int ret; + + /* Return error if store is read-only */ + if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { + usb_mtp_queue_result(s, RES_STORE_READ_ONLY, + trans, 0, 0, 0, 0); + return; + } + + if (format_code != 0) { + usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, + trans, 0, 0, 0, 0); + return; + } + + if (handle == 0xFFFFFFF) { + 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, + trans, 0, 0, 0, 0); + return; + } + + ret = usb_mtp_deletefn(s, o, trans); + if (ret == PARTIAL_DELETE) { + usb_mtp_queue_result(s, RES_PARTIAL_DELETE, + trans, 0, 0, 0, 0); + return; + } else if (ret == READ_ONLY) { + usb_mtp_queue_result(s, RES_STORE_READ_ONLY, trans, + 0, 0, 0, 0); + return; + } else { + usb_mtp_queue_result(s, RES_OK, trans, + 0, 0, 0, 0); + return; + } +} + static void usb_mtp_command(MTPState *s, MTPControl *c) { MTPData *data_in = NULL; @@ -1239,6 +1354,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) return; } break; + case CMD_DELETE_OBJECT: + usb_mtp_object_delete(s, c->argv[0], c->argv[1], c->trans); + return; case CMD_GET_PARTIAL_OBJECT: o = usb_mtp_object_lookup(s, c->argv[0]); if (o == NULL) { @@ -1545,6 +1663,10 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp) return; } s->desc = strrchr(s->root, '/'); + /* Mark store as RW */ + if (!s->readonly) { + s->flags |= (1 << MTP_FLAG_WRITABLE); + } if (s->desc && s->desc[0]) { s->desc = g_strdup(s->desc + 1); } else { @@ -1567,6 +1689,7 @@ static const VMStateDescription vmstate_usb_mtp = { static Property mtp_properties[] = { DEFINE_PROP_STRING("x-root", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), + DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), DEFINE_PROP_END_OF_LIST(), }; |