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>
This commit is contained in:
parent
5d13ebeacc
commit
ec6206a68f
123
hw/usb/dev-mtp.c
123
hw/usb/dev-mtp.c
@ -46,6 +46,7 @@ enum mtp_code {
|
|||||||
CMD_GET_OBJECT_HANDLES = 0x1007,
|
CMD_GET_OBJECT_HANDLES = 0x1007,
|
||||||
CMD_GET_OBJECT_INFO = 0x1008,
|
CMD_GET_OBJECT_INFO = 0x1008,
|
||||||
CMD_GET_OBJECT = 0x1009,
|
CMD_GET_OBJECT = 0x1009,
|
||||||
|
CMD_DELETE_OBJECT = 0x100b,
|
||||||
CMD_GET_PARTIAL_OBJECT = 0x101b,
|
CMD_GET_PARTIAL_OBJECT = 0x101b,
|
||||||
CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801,
|
CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801,
|
||||||
CMD_GET_OBJECT_PROP_DESC = 0x9802,
|
CMD_GET_OBJECT_PROP_DESC = 0x9802,
|
||||||
@ -62,6 +63,8 @@ enum mtp_code {
|
|||||||
RES_INVALID_STORAGE_ID = 0x2008,
|
RES_INVALID_STORAGE_ID = 0x2008,
|
||||||
RES_INVALID_OBJECT_HANDLE = 0x2009,
|
RES_INVALID_OBJECT_HANDLE = 0x2009,
|
||||||
RES_INVALID_OBJECT_FORMAT_CODE = 0x200b,
|
RES_INVALID_OBJECT_FORMAT_CODE = 0x200b,
|
||||||
|
RES_STORE_READ_ONLY = 0x200e,
|
||||||
|
RES_PARTIAL_DELETE = 0x2012,
|
||||||
RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014,
|
RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014,
|
||||||
RES_INVALID_PARENT_OBJECT = 0x201a,
|
RES_INVALID_PARENT_OBJECT = 0x201a,
|
||||||
RES_INVALID_PARAMETER = 0x201d,
|
RES_INVALID_PARAMETER = 0x201d,
|
||||||
@ -172,6 +175,7 @@ struct MTPState {
|
|||||||
MTPControl *result;
|
MTPControl *result;
|
||||||
uint32_t session;
|
uint32_t session;
|
||||||
uint32_t next_handle;
|
uint32_t next_handle;
|
||||||
|
bool readonly;
|
||||||
|
|
||||||
QTAILQ_HEAD(, MTPObject) objects;
|
QTAILQ_HEAD(, MTPObject) objects;
|
||||||
#ifdef CONFIG_INOTIFY1
|
#ifdef CONFIG_INOTIFY1
|
||||||
@ -799,6 +803,7 @@ static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c)
|
|||||||
CMD_GET_NUM_OBJECTS,
|
CMD_GET_NUM_OBJECTS,
|
||||||
CMD_GET_OBJECT_HANDLES,
|
CMD_GET_OBJECT_HANDLES,
|
||||||
CMD_GET_OBJECT_INFO,
|
CMD_GET_OBJECT_INFO,
|
||||||
|
CMD_DELETE_OBJECT,
|
||||||
CMD_GET_OBJECT,
|
CMD_GET_OBJECT,
|
||||||
CMD_GET_PARTIAL_OBJECT,
|
CMD_GET_PARTIAL_OBJECT,
|
||||||
CMD_GET_OBJECT_PROPS_SUPPORTED,
|
CMD_GET_OBJECT_PROPS_SUPPORTED,
|
||||||
@ -1113,6 +1118,116 @@ static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c,
|
|||||||
return d;
|
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)
|
static void usb_mtp_command(MTPState *s, MTPControl *c)
|
||||||
{
|
{
|
||||||
MTPData *data_in = NULL;
|
MTPData *data_in = NULL;
|
||||||
@ -1239,6 +1354,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CMD_DELETE_OBJECT:
|
||||||
|
usb_mtp_object_delete(s, c->argv[0], c->argv[1], c->trans);
|
||||||
|
return;
|
||||||
case CMD_GET_PARTIAL_OBJECT:
|
case CMD_GET_PARTIAL_OBJECT:
|
||||||
o = usb_mtp_object_lookup(s, c->argv[0]);
|
o = usb_mtp_object_lookup(s, c->argv[0]);
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
@ -1545,6 +1663,10 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s->desc = strrchr(s->root, '/');
|
s->desc = strrchr(s->root, '/');
|
||||||
|
/* Mark store as RW */
|
||||||
|
if (!s->readonly) {
|
||||||
|
s->flags |= (1 << MTP_FLAG_WRITABLE);
|
||||||
|
}
|
||||||
if (s->desc && s->desc[0]) {
|
if (s->desc && s->desc[0]) {
|
||||||
s->desc = g_strdup(s->desc + 1);
|
s->desc = g_strdup(s->desc + 1);
|
||||||
} else {
|
} else {
|
||||||
@ -1567,6 +1689,7 @@ static const VMStateDescription vmstate_usb_mtp = {
|
|||||||
static Property mtp_properties[] = {
|
static Property mtp_properties[] = {
|
||||||
DEFINE_PROP_STRING("x-root", MTPState, root),
|
DEFINE_PROP_STRING("x-root", MTPState, root),
|
||||||
DEFINE_PROP_STRING("desc", MTPState, desc),
|
DEFINE_PROP_STRING("desc", MTPState, desc),
|
||||||
|
DEFINE_PROP_BOOL("readonly", MTPState, readonly, true),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user