/* Fill info from dm_ioctl structure. Look at DM_EXISTS_FLAG*/
int dm_task_get_info(struct dm_task *dmt, struct dm_info *info)
{
	if (!dmt->dmi.v4)
		return 0;

	memset(info, 0, sizeof(*info));

	info->exists = dmt->dmi.v4->flags & DM_EXISTS_FLAG ? 1 : 0;
	if (!info->exists)
		return 1;

	info->suspended = dmt->dmi.v4->flags & DM_SUSPEND_FLAG ? 1 : 0;
	info->read_only = dmt->dmi.v4->flags & DM_READONLY_FLAG ? 1 : 0;
	info->live_table = dmt->dmi.v4->flags & DM_ACTIVE_PRESENT_FLAG ? 1 : 0;
	info->inactive_table = dmt->dmi.v4->flags & DM_INACTIVE_PRESENT_FLAG ?
	    1 : 0;
	info->target_count = dmt->dmi.v4->target_count;
	info->open_count = dmt->dmi.v4->open_count;
	info->event_nr = dmt->dmi.v4->event_nr;

	nbsd_get_dm_major(&info->major, DM_BLOCK_MAJOR); /* get netbsd dm device major number */
	info->minor = MINOR(dmt->dmi.v4->dev);

	return 1;
}
static int _control_device_number(uint32_t *major, uint32_t *minor)
{

	nbsd_get_dm_major(major, DM_CHAR_MAJOR);

	*minor = 0;

	return 1;
}
int dm_format_dev(char *buf, int bufsize, uint32_t dev_major,
		  uint32_t dev_minor)
{
	int r;
	uint32_t major, dm_major;
	char *name;
	mode_t mode;
	dev_t dev;
	size_t val_len,i;
	struct kinfo_drivers *kd;

	mode = 0;

	nbsd_get_dm_major(&dm_major, DM_BLOCK_MAJOR);

	if (bufsize < 8)
		return 0;

	if (sysctlbyname("kern.drivers",NULL,&val_len,NULL,0) < 0) {
		printf("sysctlbyname failed");
		return 0;
	}

	if ((kd = malloc (val_len)) == NULL){
		printf("malloc kd info error\n");
		return 0;
	}

	if (sysctlbyname("kern.drivers", kd, &val_len, NULL, 0) < 0) {
		printf("sysctlbyname failed kd");
		return 0;
	}

	for (i = 0, val_len /= sizeof(*kd); i < val_len; i++){
		if (kd[i].d_cmajor == dev_major) {
			major = kd[i].d_bmajor;
			break;
		}
	}

	dev = MKDEV(major,dev_minor);

	mode |= S_IFBLK;

	if ((name = devname(dev,mode)) == NULL)
		name = get_dev_name(kd[i].d_name, major, dev_minor);

	r = snprintf(buf, (size_t) bufsize, "/dev/%s",name);

	free(kd);

	if (r < 0 || r > bufsize - 1 || name == NULL)
		return 0;

	return 1;
}
/* Check if major is device-mapper block device major number */
int dm_is_dm_major(uint32_t major)
{
	uint32_t dm_major;

	nbsd_get_dm_major(&dm_major, DM_BLOCK_MAJOR);

	if (major == dm_major)
		return 1;

	return 0;
}
struct dm_ioctl*
nbsd_dm_dict_to_dmi(prop_dictionary_t dm_dict,const int cmd)
{
	struct dm_ioctl *dmi;
	prop_array_t ver;
	
	size_t i;
	int r;
	char *name, *uuid;
	uint32_t major,minor;
	
	name = NULL;
	uuid = NULL;
	minor = 0;
	
	nbsd_get_dm_major(&major, DM_BLOCK_MAJOR);
	
	if (!(dmi = dm_malloc(DMI_SIZE)))
		return NULL;

	memset(dmi,0,DMI_SIZE);
	
	prop_dictionary_get_int32(dm_dict, DM_IOCTL_OPEN, &dmi->open_count);
	prop_dictionary_get_uint32(dm_dict, DM_IOCTL_EVENT, &dmi->event_nr);
	prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &dmi->flags);
	prop_dictionary_get_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, 
		&dmi->target_count);

	if (prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor))
		dmi->dev = MKDEV(major, minor);
	else
		dmi->dev = 0;
	
	/* Copy name and uuid to dm_ioctl. */
	if (prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME,
		(const char **)&name)){
		strlcpy(dmi->name, name, DM_NAME_LEN);
	} else
		dmi->name[0] = '\0';
	
	if (prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID,
		(const char **)&uuid)){
		strlcpy(dmi->uuid, uuid, DM_UUID_LEN);
	}  else
		dmi->uuid[0] = '\0';

	/* dmi parsing values, size of dmi block and offset to data. */
	dmi->data_size  = DMI_SIZE;
	dmi->data_start = sizeof(struct dm_ioctl);
	
	/* Get kernel version from dm_dict. */
	ver = prop_dictionary_get(dm_dict,DM_IOCTL_VERSION);
	
	for(i=0; i<3; i++)
		prop_array_get_uint32(ver,i,&dmi->version[i]);

	switch (cmd){

	case DM_LIST_VERSIONS:
		r = dm_list_versions(dm_dict,dmi);
		if (r >= 0)
			dmi->target_count = r;
		break;

	case DM_LIST_DEVICES:
		r = dm_list_devices(dm_dict,dmi);
		if (r >= 0)
			dmi->target_count = r;
		break;	

	case DM_TABLE_STATUS:
		r = dm_table_status(dm_dict,dmi);
		if (r >= 0)
			dmi->target_count = r;
		break;	

	case DM_TABLE_DEPS:
		r = dm_dev_deps(dm_dict,dmi);
		if (r >= 0)
			dmi->target_count = r;
		break;	
	}	
	
	return dmi;
}
/*
 * List all available dm devices in system. 
 */	
static int
dm_list_devices(prop_dictionary_t dm_dict, struct dm_ioctl *dmi)
{
	struct dm_name_list *dml,*odml;
	
	prop_array_t targets;
	prop_dictionary_t target_dict;
	prop_object_iterator_t iter;

	uint32_t minor;
	uint32_t major;
	
	char *name;
	size_t j,slen,rec_size;

	odml = NULL;
	name = NULL;
	minor = 0;
	j = 0;

	nbsd_get_dm_major(&major,DM_BLOCK_MAJOR);
		
	dml = (struct dm_name_list *)((uint8_t *)dmi + dmi->data_start);

	if ((targets = prop_dictionary_get(dm_dict,DM_IOCTL_CMD_DATA))){

		iter = prop_array_iterator(targets);
		if (!iter)
			err(EXIT_FAILURE,"dm_list_devices %s",__func__);

		while((target_dict = prop_object_iterator_next(iter)) != NULL){

			prop_dictionary_get_cstring_nocopy(target_dict,
			    DM_DEV_NAME,(const char **)&name);

			prop_dictionary_get_uint32(target_dict,DM_DEV_DEV,&minor);

			dml->dev = MKDEV(major,minor);
			
			slen = strlen(name) + 1;
			rec_size = sizeof(struct dm_name_list) + slen + 1;

			if (rec_size > dmi->data_size)
				return -ENOMEM;
			
			dml->next = rec_size;
			
			strlcpy(dml->name,name,slen);
			
			odml = dml;
			
			dml =(struct dm_name_list *)((uint8_t *)dml + rec_size);

			j++;
		}

		if (odml != NULL)
			odml->next = 0;
	}
	prop_object_iterator_release(iter);
	return j;
}
/* Parse given dm task structure to proplib dictionary.  */
static int _flatten(struct dm_task *dmt, prop_dictionary_t dm_dict)
{
	prop_array_t cmd_array;
	prop_dictionary_t target_spec;

	struct target *t;

	size_t len;
	char type[DM_MAX_TYPE_NAME];

	uint32_t major, flags;
	int count = 0;
	char *str = NULL;
	const int (*version)[3];

	flags = 0;
	version = &_cmd_data_v4[dmt->type].version;

	cmd_array = prop_array_create();

	for (t = dmt->head; t; t = t->next) {
		target_spec = prop_dictionary_create();

		prop_dictionary_set_uint64(target_spec,DM_TABLE_START,t->start);
		prop_dictionary_set_uint64(target_spec,DM_TABLE_LENGTH,t->length);

		strlcpy(type,t->type,DM_MAX_TYPE_NAME);

		prop_dictionary_set_cstring(target_spec,DM_TABLE_TYPE,type);
		prop_dictionary_set_cstring(target_spec,DM_TABLE_PARAMS,t->params);

		prop_dictionary_get_cstring(target_spec,
		    DM_TABLE_PARAMS, (char **) &str);

		prop_array_set(cmd_array,count,target_spec);

		prop_object_release(target_spec);

		count++;
	}


	if (count && (dmt->sector || dmt->message)) {
		log_error("targets and message are incompatible");
		return -1;
	}

	if (count && dmt->newname) {
		log_error("targets and newname are incompatible");
		return -1;
	}

	if (count && dmt->geometry) {
		log_error("targets and geometry are incompatible");
		return -1;
	}

	if (dmt->newname && (dmt->sector || dmt->message)) {
		log_error("message and newname are incompatible");
		return -1;
	}

	if (dmt->newname && dmt->geometry) {
		log_error("geometry and newname are incompatible");
		return -1;
	}

	if (dmt->geometry && (dmt->sector || dmt->message)) {
		log_error("geometry and message are incompatible");
		return -1;
	}

	if (dmt->sector && !dmt->message) {
		log_error("message is required with sector");
		return -1;
	}

	if (dmt->newname)
		len += strlen(dmt->newname) + 1;

	if (dmt->message)
		len += sizeof(struct dm_target_msg) + strlen(dmt->message) + 1;

	if (dmt->geometry)
		len += strlen(dmt->geometry) + 1;

	nbsd_dmi_add_version((*version), dm_dict);

	nbsd_get_dm_major(&major, DM_BLOCK_MAJOR);
	/* 
	 * Only devices with major which is equal to netbsd dm major 
	 * dm devices in NetBSD can't have more majors then one assigned to dm.
	 */
	if (dmt->major != major && dmt->major != -1)
		return -1;

	if (dmt->minor >= 0) {
		flags |= DM_PERSISTENT_DEV_FLAG;

		prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmt->minor);
	}

	/* Set values to dictionary. */
	if (dmt->dev_name)
		prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmt->dev_name);

	if (dmt->uuid)
		prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmt->uuid);

	if (dmt->type == DM_DEVICE_SUSPEND)
		flags |= DM_SUSPEND_FLAG;
	if (dmt->no_flush)
		flags |= DM_NOFLUSH_FLAG;
	if (dmt->read_only)
		flags |= DM_READONLY_FLAG;
	if (dmt->skip_lockfs)
		flags |= DM_SKIP_LOCKFS_FLAG;

	if (dmt->query_inactive_table) {
		if (_dm_version_minor < 16)
			log_warn("WARNING: Inactive table query unsupported "
				 "by kernel.  It will use live table.");
		flags |= DM_QUERY_INACTIVE_TABLE_FLAG;
	}

	prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);

	prop_dictionary_set_uint32(dm_dict, DM_IOCTL_EVENT, dmt->event_nr);

	if (dmt->newname)
		prop_array_set_cstring(cmd_array, 0, dmt->newname);

	/* Add array for all COMMAND specific data. */
	prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
	prop_object_release(cmd_array);

	return 0;
}
/* Parse given dm task structure to proplib dictionary.  */
static int _flatten(struct dm_task *dmt, libdm_task_t task)
{
	libdm_cmd_t cmd;
	libdm_table_t table;

	struct target *t;

	size_t len;
	char type[DM_MAX_TYPE_NAME];

	uint32_t major, flags;
	int count = 0;

	flags = 0;

	cmd = libdm_cmd_create();

	for (t = dmt->head; t; t = t->next) {
		strlcpy(type,t->type,DM_MAX_TYPE_NAME);

		table = libdm_table_create();

		libdm_table_set_start(t->start, table);
		libdm_table_set_length(t->length, table);
		libdm_table_set_target(type, table);
		libdm_table_set_params(t->params, table);
		libdm_cmd_set_table(table, cmd);

		libdm_table_destroy(table);

		count++;
	}

	if (count && (dmt->sector || dmt->message)) {
		log_error("targets and message are incompatible");
		return -1;
	}

	if (count && dmt->newname) {
		log_error("targets and newname are incompatible");
		return -1;
	}

	if (count && dmt->geometry) {
		log_error("targets and geometry are incompatible");
		return -1;
	}

	if (dmt->newname && (dmt->sector || dmt->message)) {
		log_error("message and newname are incompatible");
		return -1;
	}

	if (dmt->newname && dmt->geometry) {
		log_error("geometry and newname are incompatible");
		return -1;
	}

	if (dmt->geometry && (dmt->sector || dmt->message)) {
		log_error("geometry and message are incompatible");
		return -1;
	}

	if (dmt->sector && !dmt->message) {
		log_error("message is required with sector");
		return -1;
	}

	if (dmt->newname)
		len += strlen(dmt->newname) + 1;

	if (dmt->message)
		len += sizeof(struct dm_target_msg) + strlen(dmt->message) + 1;

	if (dmt->geometry)
		len += strlen(dmt->geometry) + 1;

	nbsd_get_dm_major(&major, DM_BLOCK_MAJOR);
	/*
	 * Only devices with major which is equal to netbsd dm major
	 * dm devices in NetBSD can't have more majors then one assigned to dm.
	 */
	if (dmt->major != major && dmt->major != -1)
		return -1;

	if (dmt->minor >= 0) {
		flags |= DM_PERSISTENT_DEV_FLAG;
		libdm_task_set_minor(dmt->minor, task);
	}

	/* Set values to dictionary. */
	if (dmt->dev_name)
		libdm_task_set_name(dmt->dev_name, task);

	if (dmt->uuid)
		libdm_task_set_uuid(dmt->uuid, task);

	if (dmt->type == DM_DEVICE_SUSPEND)
		flags |= DM_SUSPEND_FLAG;
	if (dmt->no_flush)
		flags |= DM_NOFLUSH_FLAG;
	if (dmt->read_only)
		flags |= DM_READONLY_FLAG;
	if (dmt->skip_lockfs)
		flags |= DM_SKIP_LOCKFS_FLAG;

	if (dmt->query_inactive_table) {
		if (_dm_version_minor < 16)
			log_warn("WARNING: Inactive table query unsupported "
				 "by kernel.  It will use live table.");
		flags |= DM_QUERY_INACTIVE_TABLE_FLAG;
	}

	libdm_task_set_flags(task, flags);

//	prop_dictionary_set_uint32(dm_dict, DM_IOCTL_EVENT, dmt->event_nr);

	if (dmt->newname)
		libdm_dev_set_newname(dmt->newname, cmd);

	/* Add array for all COMMAND specific data. */
	libdm_task_set_cmd(cmd, task);
	libdm_cmd_destroy(cmd);

	return 0;
}