On 31.01.2012, at 22:49, Alexander Graf wrote: > This patch implements all ioctls currently implemented by device mapper, > enabling us to run dmsetup and kpartx inside of linux-user.
Hi Alasdair, Could you please have a quick glimpse through this wrapper and check that I handled the ioctls correctly? Thanks! Alex > > Signed-off-by: Alexander Graf <ag...@suse.de> > --- > linux-user/ioctls.h | 33 +++++++ > linux-user/syscall.c | 226 ++++++++++++++++++++++++++++++++++++++++++++ > linux-user/syscall_defs.h | 18 ++++ > linux-user/syscall_types.h | 36 +++++++ > 4 files changed, 313 insertions(+), 0 deletions(-) > > diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h > index 6514502..a9d333a 100644 > --- a/linux-user/ioctls.h > +++ b/linux-user/ioctls.h > @@ -345,3 +345,36 @@ > IOCTL(VT_SETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode))) > IOCTL(VT_RELDISP, 0, TYPE_INT) > IOCTL(VT_DISALLOCATE, 0, TYPE_INT) > + > + IOCTL(DM_VERSION, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_REMOVE_ALL, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_LIST_DEVICES, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_CREATE, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_REMOVE, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_RENAME, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_SUSPEND, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_STATUS, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_WAIT, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_TABLE_LOAD, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_TABLE_CLEAR, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_TABLE_DEPS, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_TABLE_STATUS, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_LIST_VERSIONS,IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_TARGET_MSG, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + IOCTL_SPECIAL(DM_DEV_SET_GEOMETRY, IOC_RW, do_ioctl_dm, > + MK_PTR(MK_STRUCT(STRUCT_dm_ioctl))) > + > diff --git a/linux-user/syscall.c b/linux-user/syscall.c > index 2bf9e7e..27a3131 100644 > --- a/linux-user/syscall.c > +++ b/linux-user/syscall.c > @@ -95,6 +95,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, > #endif > #include <linux/fb.h> > #include <linux/vt.h> > +#include <linux/dm-ioctl.h> > #include "linux_loop.h" > #include "cpu-uname.h" > > @@ -3317,6 +3318,231 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, > uint8_t *buf_temp, > return ret; > } > > +static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, > + abi_long cmd, abi_long arg) > +{ > + void *argptr; > + struct dm_ioctl *host_dm; > + abi_long guest_data; > + uint32_t guest_data_size; > + int target_size; > + const argtype *arg_type = ie->arg_type; > + abi_long ret; > + void *big_buf = NULL; > + char *host_data; > + > + arg_type++; > + target_size = thunk_type_size(arg_type, 0); > + argptr = lock_user(VERIFY_READ, arg, target_size, 1); > + if (!argptr) { > + ret = -TARGET_EFAULT; > + goto out; > + } > + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); > + unlock_user(argptr, arg, 0); > + > + /* buf_temp is too small, so fetch things into a bigger buffer */ > + big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2); > + memcpy(big_buf, buf_temp, target_size); > + buf_temp = big_buf; > + host_dm = big_buf; > + > + guest_data = arg + host_dm->data_start; > + if ((guest_data - arg) < 0) { > + ret = -EINVAL; > + goto out; > + } > + guest_data_size = host_dm->data_size - host_dm->data_start; > + host_data = (char*)host_dm + host_dm->data_start; > + > + argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1); > + switch (ie->host_cmd) { > + case DM_REMOVE_ALL: > + case DM_LIST_DEVICES: > + case DM_DEV_CREATE: > + case DM_DEV_REMOVE: > + case DM_DEV_SUSPEND: > + case DM_DEV_STATUS: > + case DM_DEV_WAIT: > + case DM_TABLE_STATUS: > + case DM_TABLE_CLEAR: > + case DM_TABLE_DEPS: > + case DM_LIST_VERSIONS: > + /* no input data */ > + break; > + case DM_DEV_RENAME: > + case DM_DEV_SET_GEOMETRY: > + /* data contains only strings */ > + memcpy(host_data, argptr, guest_data_size); > + break; > + case DM_TARGET_MSG: > + memcpy(host_data, argptr, guest_data_size); > + *(uint64_t*)host_data = tswap64(*(uint64_t*)argptr); > + break; > + case DM_TABLE_LOAD: > + { > + void *gspec = argptr; > + void *cur_data = host_data; > + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; > + int spec_size = thunk_type_size(arg_type, 0); > + int i; > + > + for (i = 0; i < host_dm->target_count; i++) { > + struct dm_target_spec *spec = cur_data; > + uint32_t next; > + int slen; > + > + thunk_convert(spec, gspec, arg_type, THUNK_HOST); > + slen = strlen((char*)gspec + spec_size) + 1; > + next = spec->next; > + spec->next = sizeof(*spec) + slen; > + strcpy((char*)&spec[1], gspec + spec_size); > + gspec += next; > + cur_data += spec->next; > + } > + break; > + } > + default: > + ret = -TARGET_EINVAL; > + goto out; > + } > + unlock_user(argptr, guest_data, 0); > + > + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); > + if (!is_error(ret)) { > + guest_data = arg + host_dm->data_start; > + guest_data_size = host_dm->data_size - host_dm->data_start; > + argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0); > + switch (ie->host_cmd) { > + case DM_REMOVE_ALL: > + case DM_DEV_CREATE: > + case DM_DEV_REMOVE: > + case DM_DEV_RENAME: > + case DM_DEV_SUSPEND: > + case DM_DEV_STATUS: > + case DM_TABLE_LOAD: > + case DM_TABLE_CLEAR: > + case DM_TARGET_MSG: > + case DM_DEV_SET_GEOMETRY: > + /* no return data */ > + break; > + case DM_LIST_DEVICES: > + { > + struct dm_name_list *nl = (void*)host_dm + host_dm->data_start; > + uint32_t remaining_data = guest_data_size; > + void *cur_data = argptr; > + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) }; > + int nl_size = 12; /* can't use thunk_size due to alignment */ > + > + while (1) { > + uint32_t next = nl->next; > + if (next) { > + nl->next = nl_size + (strlen(nl->name) + 1); > + } > + if (remaining_data < nl->next) { > + host_dm->flags |= DM_BUFFER_FULL_FLAG; > + break; > + } > + thunk_convert(cur_data, nl, arg_type, THUNK_TARGET); > + strcpy(cur_data + nl_size, nl->name); > + cur_data += nl->next; > + remaining_data -= nl->next; > + if (!next) { > + break; > + } > + nl = (void*)nl + next; > + } > + break; > + } > + case DM_DEV_WAIT: > + case DM_TABLE_STATUS: > + { > + struct dm_target_spec *spec = (void*)host_dm + > host_dm->data_start; > + void *cur_data = argptr; > + const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; > + int spec_size = thunk_type_size(arg_type, 0); > + int i; > + > + for (i = 0; i < host_dm->target_count; i++) { > + uint32_t next = spec->next; > + int slen = strlen((char*)&spec[1]) + 1; > + spec->next = (cur_data - argptr) + spec_size + slen; > + if (guest_data_size < spec->next) { > + host_dm->flags |= DM_BUFFER_FULL_FLAG; > + break; > + } > + thunk_convert(cur_data, spec, arg_type, THUNK_TARGET); > + strcpy(cur_data + spec_size, (char*)&spec[1]); > + cur_data = argptr + spec->next; > + spec = (void*)host_dm + host_dm->data_start + next; > + } > + break; > + } > + case DM_TABLE_DEPS: > + { > + void *hdata = (void*)host_dm + host_dm->data_start; > + int count = *(uint32_t*)hdata; > + uint64_t *hdev = hdata + 8; > + uint64_t *gdev = argptr + 8; > + int i; > + > + *(uint32_t*)argptr = tswap32(count); > + for (i = 0; i < count; i++) { > + *gdev = tswap64(*hdev); > + gdev++; > + hdev++; > + } > + break; > + } > + case DM_LIST_VERSIONS: > + { > + struct dm_target_versions *vers = (void*)host_dm + > host_dm->data_start; > + uint32_t remaining_data = guest_data_size; > + void *cur_data = argptr; > + const argtype arg_type[] = { > MK_STRUCT(STRUCT_dm_target_versions) }; > + int vers_size = thunk_type_size(arg_type, 0); > + > + while (1) { > + uint32_t next = vers->next; > + if (next) { > + vers->next = vers_size + (strlen(vers->name) + 1); > + } > + if (remaining_data < vers->next) { > + host_dm->flags |= DM_BUFFER_FULL_FLAG; > + break; > + } > + thunk_convert(cur_data, vers, arg_type, THUNK_TARGET); > + strcpy(cur_data + vers_size, vers->name); > + cur_data += vers->next; > + remaining_data -= vers->next; > + if (!next) { > + break; > + } > + vers = (void*)vers + next; > + } > + break; > + } > + default: > + ret = -TARGET_EINVAL; > + goto out; > + } > + unlock_user(argptr, guest_data, guest_data_size); > + > + argptr = lock_user(VERIFY_WRITE, arg, target_size, 0); > + if (!argptr) { > + ret = -TARGET_EFAULT; > + goto out; > + } > + thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET); > + unlock_user(argptr, arg, target_size); > + } > +out: > + if (big_buf) { > + free(big_buf); > + } > + return ret; > +} > + > static IOCTLEntry ioctl_entries[] = { > #define IOCTL(cmd, access, ...) \ > { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, > diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h > index 2857805..f5e0c41 100644 > --- a/linux-user/syscall_defs.h > +++ b/linux-user/syscall_defs.h > @@ -989,6 +989,24 @@ struct target_pollfd { > #define TARGET_VT_RELDISP 0x5605 > #define TARGET_VT_DISALLOCATE 0x5608 > > +/* device mapper */ > +#define TARGET_DM_VERSION TARGET_IOWRU(0xfd, 0x00) > +#define TARGET_DM_REMOVE_ALL TARGET_IOWRU(0xfd, 0x01) > +#define TARGET_DM_LIST_DEVICES TARGET_IOWRU(0xfd, 0x02) > +#define TARGET_DM_DEV_CREATE TARGET_IOWRU(0xfd, 0x03) > +#define TARGET_DM_DEV_REMOVE TARGET_IOWRU(0xfd, 0x04) > +#define TARGET_DM_DEV_RENAME TARGET_IOWRU(0xfd, 0x05) > +#define TARGET_DM_DEV_SUSPEND TARGET_IOWRU(0xfd, 0x06) > +#define TARGET_DM_DEV_STATUS TARGET_IOWRU(0xfd, 0x07) > +#define TARGET_DM_DEV_WAIT TARGET_IOWRU(0xfd, 0x08) > +#define TARGET_DM_TABLE_LOAD TARGET_IOWRU(0xfd, 0x09) > +#define TARGET_DM_TABLE_CLEAR TARGET_IOWRU(0xfd, 0x0a) > +#define TARGET_DM_TABLE_DEPS TARGET_IOWRU(0xfd, 0x0b) > +#define TARGET_DM_TABLE_STATUS TARGET_IOWRU(0xfd, 0x0c) > +#define TARGET_DM_LIST_VERSIONS TARGET_IOWRU(0xfd, 0x0d) > +#define TARGET_DM_TARGET_MSG TARGET_IOWRU(0xfd, 0x0e) > +#define TARGET_DM_DEV_SET_GEOMETRY TARGET_IOWRU(0xfd, 0x0f) > + > /* from asm/termbits.h */ > > #define TARGET_NCC 8 > diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h > index c370125..fb8c9c9 100644 > --- a/linux-user/syscall_types.h > +++ b/linux-user/syscall_types.h > @@ -186,6 +186,42 @@ STRUCT(vt_mode, > TYPE_SHORT, /* acqsig */ > TYPE_SHORT) /* frsig */ > > +STRUCT(dm_ioctl, > + MK_ARRAY(TYPE_INT, 3), /* version */ > + TYPE_INT, /* data_size */ > + TYPE_INT, /* data_start */ > + TYPE_INT, /* target_count*/ > + TYPE_INT, /* open_count */ > + TYPE_INT, /* flags */ > + TYPE_INT, /* event_nr */ > + TYPE_INT, /* padding */ > + TYPE_ULONGLONG, /* dev */ > + MK_ARRAY(TYPE_CHAR, 128), /* name */ > + MK_ARRAY(TYPE_CHAR, 129), /* uuid */ > + MK_ARRAY(TYPE_CHAR, 7)) /* data */ > + > +STRUCT(dm_target_spec, > + TYPE_ULONGLONG, /* sector_start */ > + TYPE_ULONGLONG, /* length */ > + TYPE_INT, /* status */ > + TYPE_INT, /* next */ > + MK_ARRAY(TYPE_CHAR, 16)) /* target_type */ > + > +STRUCT(dm_target_deps, > + TYPE_INT, /* count */ > + TYPE_INT) /* padding */ > + > +STRUCT(dm_name_list, > + TYPE_ULONGLONG, /* dev */ > + TYPE_INT) /* next */ > + > +STRUCT(dm_target_versions, > + TYPE_INT, /* next */ > + MK_ARRAY(TYPE_INT, 3)) /* version*/ > + > +STRUCT(dm_target_msg, > + TYPE_ULONGLONG) /* sector */ > + > STRUCT(fiemap_extent, > TYPE_ULONGLONG, /* fe_logical */ > TYPE_ULONGLONG, /* fe_physical */ > -- > 1.6.0.2 > >