func: 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;
  
  }
  