Re: [Qemu-devel] [PATCH v4 3/5] usb-mtp: Support delete of mtp objects
On Tue, Feb 20, 2018 at 05:59:02PM -0500, Bandan Das wrote: > 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 > --- > hw/usb/dev-mtp.c | 123 > +++ > 1 file changed, 123 insertions(+) > > 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, Should we not advertize this in the first place if the device is readonly. > 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 == 0xFFF) { > +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 == RE
[Qemu-devel] [PATCH v4 3/5] 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 --- hw/usb/dev-mtp.c | 123 +++ 1 file changed, 123 insertions(+) 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 == 0xFFF) { +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) ret