struct dm_list *tag_list_copy(struct dm_pool *p, struct dm_list *tag_list)
{
	struct dm_list *list;
	lvm_str_list_t *lsl;
	struct str_list *sl;

	if (!(list = dm_pool_zalloc(p, sizeof(*list)))) {
		log_errno(ENOMEM, "Memory allocation fail for dm_list.");
		return NULL;
	}
	dm_list_init(list);

	dm_list_iterate_items(sl, tag_list) {
		if (!(lsl = dm_pool_zalloc(p, sizeof(*lsl)))) {
			log_errno(ENOMEM,
				"Memory allocation fail for lvm_lv_list.");
			return NULL;
		}
		if (!(lsl->str = dm_pool_strdup(p, sl->str))) {
			log_errno(ENOMEM,
				"Memory allocation fail for lvm_lv_list->str.");
			return NULL;
		}
		dm_list_add(list, &lsl->list);
	}
	return list;
}
Exemplo n.º 2
0
static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
{
	struct config_value *new_cv;

	if (!v)
		return NULL;

	if (!(new_cv = _create_value(mem))) {
		log_error("Failed to clone config value.");
		return NULL;
	}

	new_cv->type = v->type;
	if (v->type == CFG_STRING) {
		if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) {
			log_error("Failed to clone config string value.");
			return NULL;
		}
	} else
		new_cv->v = v->v;

	if (v->next && !(new_cv->next = _clone_config_value(mem, v->next)))
		return_NULL;

	return new_cv;
}
Exemplo n.º 3
0
/*
 * public interface
 */
struct dm_config_tree *config_file_open(const char *filename, int keep_open)
{
	struct dm_config_tree *cft = dm_config_create();
	struct config_file *cf;
	if (!cft)
		return NULL;

	cf = dm_pool_zalloc(cft->mem, sizeof(struct config_file));
	if (!cf) goto fail;

	cf->timestamp = 0;
	cf->exists = 0;
	cf->keep_open = keep_open;
	dm_config_set_custom(cft, cf);

	if (filename &&
	    !(cf->filename = dm_pool_strdup(cft->mem, filename))) {
		log_error("Failed to duplicate filename.");
		goto fail;
	}

	return cft;
fail:
	dm_config_destroy(cft);
	return NULL;
}
Exemplo n.º 4
0
/*
 * dev_manager implementation.
 */
struct dev_manager *dev_manager_create(struct cmd_context *cmd,
				       const char *vg_name)
{
	struct dm_pool *mem;
	struct dev_manager *dm;

	if (!(mem = dm_pool_create("dev_manager", 16 * 1024)))
		return_NULL;

	if (!(dm = dm_pool_zalloc(mem, sizeof(*dm))))
		goto_bad;

	dm->cmd = cmd;
	dm->mem = mem;

	if (!(dm->vg_name = dm_pool_strdup(dm->mem, vg_name)))
		goto_bad;

	dm->target_state = NULL;

	dm_udev_set_sync_support(cmd->current_settings.udev_sync);

	return dm;

      bad:
	dm_pool_destroy(mem);
	return NULL;
}
Exemplo n.º 5
0
/*
 * public interface
 */
struct config_tree *create_config_tree(const char *filename, int keep_open)
{
	struct cs *c;
	struct dm_pool *mem = dm_pool_create("config", 10 * 1024);

	if (!mem) {
		log_error("Failed to allocate config pool.");
		return 0;
	}

	if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
		log_error("Failed to allocate config tree.");
		dm_pool_destroy(mem);
		return 0;
	}

	c->mem = mem;
	c->cft.root = (struct config_node *) NULL;
	c->timestamp = 0;
	c->exists = 0;
	c->keep_open = keep_open;
	c->dev = 0;
	if (filename)
		c->filename = dm_pool_strdup(c->mem, filename);
	return &c->cft;
}
Exemplo n.º 6
0
struct config_node *clone_config_node_with_mem(struct dm_pool *mem, const struct config_node *cn,
					       int siblings)
{
	struct config_node *new_cn;

	if (!cn)
		return NULL;

	if (!(new_cn = _create_node(mem))) {
		log_error("Failed to clone config node.");
		return NULL;
	}

	if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) {
		log_error("Failed to clone config node key.");
		return NULL;
	}

	if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) ||
	    (cn->child && !(new_cn->child = clone_config_node_with_mem(mem, cn->child, 1))) ||
	    (siblings && cn->sib && !(new_cn->sib = clone_config_node_with_mem(mem, cn->sib, siblings))))
		return_NULL; /* 'new_cn' released with mem pool */

	return new_cn;
}
Exemplo n.º 7
0
/* Parse replicator site element */
static int _add_site(struct lv_segment *seg,
		     const char *key,
		     const struct dm_config_node *sn)
{
	struct dm_pool *mem = seg->lv->vg->vgmem;
	const struct dm_config_node *cn;
	struct replicator_site *rsite;

	if (!(rsite = _get_site(seg->lv, key)))
		return_0;

	if (!dm_config_find_node(sn, "site_index"))
		return SEG_LOG_ERROR("Mandatory site_index is missing for");

	rsite->state = _get_state(sn, "state", REPLICATOR_STATE_PASSIVE);
	rsite->site_index = _get_config_uint32(sn, "site_index", 0);
	if (rsite->site_index > seg->rsite_index_highest)
		return SEG_LOG_ERROR("site_index=%d > highest_site_index=%d for",
				     rsite->site_index, seg->rsite_index_highest);

	rsite->fall_behind_data = _get_config_uint64(sn, "fall_behind_data", 0);
	rsite->fall_behind_ios = _get_config_uint32(sn, "fall_behind_ios", 0);
	rsite->fall_behind_timeout = _get_config_uint32(sn, "fall_behind_timeout", 0);
	rsite->op_mode = DM_REPLICATOR_SYNC;

	if (rsite->fall_behind_data ||
	    rsite->fall_behind_ios ||
	    rsite->fall_behind_timeout) {
		if (rsite->fall_behind_data && rsite->fall_behind_ios)
			return SEG_LOG_ERROR("Defined both fall_behind_data "
					     "and fall_behind_ios in");

		if (rsite->fall_behind_data && rsite->fall_behind_timeout)
			return SEG_LOG_ERROR("Defined both fall_behind_data "
					     "and fall_behind_timeout in");

		if (rsite->fall_behind_ios && rsite->fall_behind_timeout)
			return SEG_LOG_ERROR("Defined both fall_behind_ios "
					     "and fall_behind_timeout in");

		rsite->op_mode = _get_op_mode(sn, "operation_mode",
					      rsite->op_mode);
	}

	if ((cn = dm_config_find_node(sn, "volume_group"))) {
		if (!cn->v || cn->v->type != DM_CFG_STRING)
			return SEG_LOG_ERROR("volume_group must be a string in");

		if (!(rsite->vg_name = dm_pool_strdup(mem, cn->v->v.str)))
			return_0;

	} else if (rsite->site_index != 0)
		return SEG_LOG_ERROR("volume_group is mandatory for remote site in");

	return 1;
}
Exemplo n.º 8
0
int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
		      char **vgname, char **lvname, char **layer)
{
	if (mem && !(*vgname = dm_pool_strdup(mem, dmname)))
		return 0;

	_unquote(*layer = _unquote(*lvname = _unquote(*vgname)));

	return 1;
}
Exemplo n.º 9
0
/*
 * Extracts the last part of a path.
 */
static char *_create_lv_name(struct dm_pool *mem, const char *full_name)
{
	const char *ptr = strrchr(full_name, '/');

	if (!ptr)
		ptr = full_name;
	else
		ptr++;

	return dm_pool_strdup(mem, ptr);
}
Exemplo n.º 10
0
const char *lvm_errmsg(lvm_t libh)
{
	const char *rc = NULL;
	struct cmd_context *cmd = (struct cmd_context *)libh;
	struct saved_env e = store_user_env((struct cmd_context *)libh);

	const char *msg = stored_errmsg_with_clear();
	if (msg) {
		rc = dm_pool_strdup(cmd->mem, msg);
		free((void *)msg);
	}

	restore_user_env(&e);
	return rc;
}
Exemplo n.º 11
0
struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key)
{
	struct dm_config_node *cn;

	if (!(cn = _create_node(cft->mem))) {
		log_error("Failed to create config node.");
		return NULL;
	}
	if (!(cn->key = dm_pool_strdup(cft->mem, key))) {
		log_error("Failed to create config node's key.");
		return NULL;
	}
	cn->parent = NULL;
	cn->v = NULL;

	return cn;
}
int read_tags(struct dm_pool *mem, struct dm_list *tags, struct config_value *cv)
{
	if (cv->type == CFG_EMPTY_ARRAY)
		return 1;

	while (cv) {
		if (cv->type != CFG_STRING) {
			log_error("Found a tag that is not a string");
			return 0;
		}

		if (!str_list_add(mem, tags, dm_pool_strdup(mem, cv->v.str)))
			return_0;

		cv = cv->next;
	}

	return 1;
}
Exemplo n.º 13
0
int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
		      char **vgname, char **lvname, char **layer)
{
	if (!vgname || !lvname || !layer) {
		log_error(INTERNAL_ERROR "dm_split_lvm_name: Forbidden NULL parameter detected.");
		return 0;
	}

	if (mem && (!dmname || !(*vgname = dm_pool_strdup(mem, dmname)))) {
		log_error("Failed to duplicate lvm name.");
		return 0;
	} else if (!*vgname) {
		log_error("Missing lvm name for split.");
		return 0;
	}

	_unquote(*layer = _unquote(*lvname = _unquote(*vgname)));

	return 1;
}
Exemplo n.º 14
0
static struct replicator_site *_get_site(struct logical_volume *replicator,
					 const char *key)
{
	struct dm_pool *mem = replicator->vg->vgmem;
	struct replicator_site *rsite;

	dm_list_iterate_items(rsite, &replicator->rsites)
		if (strcasecmp(rsite->name, key) == 0)
			return rsite;

	if (!(rsite = dm_pool_zalloc(mem, sizeof(*rsite))))
		return_NULL;

	if (!(rsite->name = dm_pool_strdup(mem, key)))
		return_NULL;

	rsite->replicator = replicator;
	dm_list_init(&rsite->rdevices);
	dm_list_add(&replicator->rsites, &rsite->list);

	return rsite;
}
Exemplo n.º 15
0
/*
 * public interface
 */
struct dm_config_tree *config_open(config_source_t source,
				   const char *filename,
				   int keep_open)
{
	struct dm_config_tree *cft = dm_config_create();
	struct config_source *cs;
	struct config_file *cf;

	if (!cft)
		return NULL;

	if (!(cs = dm_pool_zalloc(cft->mem, sizeof(struct config_source)))) {
		log_error("Failed to allocate config source.");
		goto fail;
	}

	if ((source == CONFIG_FILE) || (source == CONFIG_PROFILE)) {
		if (!(cf = dm_pool_zalloc(cft->mem, sizeof(struct config_file)))) {
			log_error("Failed to allocate config file.");
			goto fail;
		}

		cf->keep_open = keep_open;
		if (filename &&
		    !(cf->filename = dm_pool_strdup(cft->mem, filename))) {
			log_error("Failed to duplicate filename.");
			goto fail;
		}

		cs->source.file = cf;
	}

	cs->type = source;
	dm_config_set_custom(cft, cs);
	return cft;
fail:
	dm_config_destroy(cft);
	return NULL;
}
Exemplo n.º 16
0
int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls)
{
	struct pool_list *pl;

	dm_list_iterate_items(pl, pls) {
		vg->extent_count +=
		    ((pl->pd.pl_blocks) / POOL_PE_SIZE);

		vg->free_count = vg->extent_count;
		vg->pv_count++;

		if (vg->name)
			continue;

		vg->name = dm_pool_strdup(mem, pl->pd.pl_pool_name);
		get_pool_vg_uuid(&vg->id, &pl->pd);
		vg->extent_size = POOL_PE_SIZE;
		vg->status |= LVM_READ | LVM_WRITE | CLUSTERED | SHARED;
		vg->max_lv = 1;
		vg->max_pv = POOL_MAX_DEVICES;
		vg->alloc = ALLOC_NORMAL;
	}
Exemplo n.º 17
0
struct config_node *create_config_node(struct config_tree *cft, const char *key)
{
	struct cs *c = (struct cs *) cft;
	struct config_node *cn;

	if (!(cn = _create_node(c->mem))) {
		log_error("Failed to create config node.");
		return NULL;
	}
	if (!(cn->key = dm_pool_strdup(c->mem, key))) {
		log_error("Failed to create config node's key.");
		return NULL;
	}
	if (!(cn->v = _create_value(c->mem))) {
		log_error("Failed to create config node's value.");
		return NULL;
	}
	cn->parent = NULL;
	cn->v->type = CFG_INT;
	cn->v->v.i = 0;
	cn->v->next = NULL;
	return cn;
}
Exemplo n.º 18
0
int vgrename(struct cmd_context *cmd, int argc, char **argv)
{
	struct vgrename_params vp = { 0 };
	struct processing_handle *handle;
	const char *vg_name_new;
	const char *vg_name_old;
	struct id id;
	int ret;

	if (argc != 2) {
		log_error("Old and new volume group names need specifying");
		return EINVALID_CMD_LINE;
	}

	vg_name_old = skip_dev_dir(cmd, argv[0], NULL);
	vg_name_new = skip_dev_dir(cmd, argv[1], NULL);

	if (!validate_vg_rename_params(cmd, vg_name_old, vg_name_new))
		return_0;

	if (!(vp.vg_name_old = dm_pool_strdup(cmd->mem, vg_name_old)))
		return_ECMD_FAILED;

	if (!(vp.vg_name_new = dm_pool_strdup(cmd->mem, vg_name_new)))
		return_ECMD_FAILED;

	/* Needed change the global VG namespace. */
	if (!lockd_gl(cmd, "ex", LDGL_UPDATE_NAMES))
		return_ECMD_FAILED;

	/*
	 * Special case where vg_name_old may be a UUID:
	 * If vg_name_old is a UUID, then process_each may
	 * translate it to an actual VG name that we don't
	 * yet know.  The lock ordering, and pre-locking,
	 * needs to be done based on VG names.  When
	 * vg_name_old is a UUID, do not do any pre-locking
	 * based on it, since it's likely to be wrong, and
	 * defer all the locking to the _single function.
	 *
	 * When it's not a UUID, we know the two VG names,
	 * and we can pre-lock the new VG name if the lock
	 * ordering wants it locked before the old VG name
	 * which will be locked by process_each.  If lock
	 * ordering wants the old name locked first, then
	 * the _single function will lock the new VG name.
	 */
	if (!(vp.old_name_is_uuid = id_read_format_try(&id, vg_name_old))) {
		if (strcmp(vg_name_new, vg_name_old) < 0) {
			vp.lock_vg_old_first = 0;
			vp.unlock_new_name = 1;

			if (!_lock_new_vg_for_rename(cmd, vg_name_new))
				return ECMD_FAILED;
		} else {
			/* The old VG is locked by process_each_vg. */
			vp.lock_vg_old_first = 1;
		}
	}

	if (!(handle = init_processing_handle(cmd))) {
		log_error("Failed to initialize processing handle.");
		return ECMD_FAILED;
	}

	handle->custom_handle = &vp;

	ret = process_each_vg(cmd, 0, NULL, vg_name_old,
			      READ_FOR_UPDATE | READ_ALLOW_EXPORTED,
			      handle, _vgrename_single);

	/* Needed if process_each_vg returns error before calling _single. */
	if (vp.unlock_new_name)
		unlock_vg(cmd, vg_name_new);

	destroy_processing_handle(cmd, handle);
	return ret;
}
Exemplo n.º 19
0
/*
 * <metadata block size> <#used metadata blocks>/<#total metadata blocks>
 * <cache block size> <#used cache blocks>/<#total cache blocks>
 * <#read hits> <#read misses> <#write hits> <#write misses>
 * <#demotions> <#promotions> <#dirty> <#features> <features>*
 * <#core args> <core args>* <policy name> <#policy args> <policy args>*
 *
 * metadata block size      : Fixed block size for each metadata block in
 *                            sectors
 * #used metadata blocks    : Number of metadata blocks used
 * #total metadata blocks   : Total number of metadata blocks
 * cache block size         : Configurable block size for the cache device
 *                            in sectors
 * #used cache blocks       : Number of blocks resident in the cache
 * #total cache blocks      : Total number of cache blocks
 * #read hits               : Number of times a READ bio has been mapped
 *                            to the cache
 * #read misses             : Number of times a READ bio has been mapped
 *                            to the origin
 * #write hits              : Number of times a WRITE bio has been mapped
 *                            to the cache
 * #write misses            : Number of times a WRITE bio has been
 *                            mapped to the origin
 * #demotions               : Number of times a block has been removed
 *                            from the cache
 * #promotions              : Number of times a block has been moved to
 *                            the cache
 * #dirty                   : Number of blocks in the cache that differ
 *                            from the origin
 * #feature args            : Number of feature args to follow
 * feature args             : 'writethrough' (optional)
 * #core args               : Number of core arguments (must be even)
 * core args                : Key/value pairs for tuning the core
 *                            e.g. migration_threshold
 *			     *policy name              : Name of the policy
 * #policy args             : Number of policy arguments to follow (must be even)
 * policy args              : Key/value pairs
 *                            e.g. sequential_threshold
 */
int dm_get_status_cache(struct dm_pool *mem, const char *params,
			struct dm_status_cache **status)
{
	int i, feature_argc;
	char *str;
	const char *p, *pp;
	struct dm_status_cache *s;

	if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_cache))))
		return_0;

	/* Read in args that have definitive placement */
	if (sscanf(params,
		   " %" PRIu32
		   " %" PRIu64 "/%" PRIu64
		   " %" PRIu32
		   " %" PRIu64 "/%" PRIu64
		   " %" PRIu64 " %" PRIu64
		   " %" PRIu64 " %" PRIu64
		   " %" PRIu64 " %" PRIu64
		   " %" PRIu64
		   " %d",
		   &s->metadata_block_size,
		   &s->metadata_used_blocks, &s->metadata_total_blocks,
		   &s->block_size, /* AKA, chunk_size */
		   &s->used_blocks, &s->total_blocks,
		   &s->read_hits, &s->read_misses,
		   &s->write_hits, &s->write_misses,
		   &s->demotions, &s->promotions,
		   &s->dirty_blocks,
		   &feature_argc) != 14)
		goto bad;

	/* Now jump to "features" section */
	if (!(p = _advance_to_next_word(params, 12)))
		goto bad;

	/* Read in features */
	for (i = 0; i < feature_argc; i++) {
		if (!strncmp(p, "writethrough ", 13))
			s->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
		else if (!strncmp(p, "writeback ", 10))
			s->feature_flags |= DM_CACHE_FEATURE_WRITEBACK;
		else
			log_error("Unknown feature in status: %s", params);

		if (!(p = _advance_to_next_word(p, 1)))
			goto bad;
	}

	/* Read in core_args. */
	if (sscanf(p, "%d ", &s->core_argc) != 1)
		goto bad;
	if (s->core_argc &&
	    (!(s->core_argv = dm_pool_zalloc(mem, sizeof(char *) * s->core_argc)) ||
	     !(p = _advance_to_next_word(p, 1)) ||
	     !(str = dm_pool_strdup(mem, p)) ||
	     !(p = _advance_to_next_word(p, s->core_argc)) ||
	     (dm_split_words(str, s->core_argc, 0, s->core_argv) != s->core_argc)))
		goto bad;

	/* Read in policy args */
	pp = p;
	if (!(p = _advance_to_next_word(p, 1)) ||
	    !(s->policy_name = dm_pool_zalloc(mem, (p - pp))))
		goto bad;
	if (sscanf(pp, "%s %d", s->policy_name, &s->policy_argc) != 2)
		goto bad;
	if (s->policy_argc &&
	    (!(s->policy_argv = dm_pool_zalloc(mem, sizeof(char *) * s->policy_argc)) ||
	     !(p = _advance_to_next_word(p, 1)) ||
	     !(str = dm_pool_strdup(mem, p)) ||
	     (dm_split_words(str, s->policy_argc, 0, s->policy_argv) != s->policy_argc)))
		goto bad;

	*status = s;
	return 1;

bad:
	log_error("Failed to parse cache params: %s", params);
	dm_pool_free(mem, s);
	*status = NULL;

	return 0;
}
Exemplo n.º 20
0
int import_pv(const struct format_type *fmt, struct dm_pool *mem,
	      struct device *dev, struct volume_group *vg,
	      struct physical_volume *pv, struct pv_disk *pvd,
	      struct vg_disk *vgd)
{
	uint64_t size;

	memset(pv, 0, sizeof(*pv));
	memcpy(&pv->id, pvd->pv_uuid, ID_LEN);

	pv->dev = dev;
	if (!*pvd->vg_name)
		pv->vg_name = fmt->orphan_vg_name;
	else if (!(pv->vg_name = dm_pool_strdup(mem, (char *)pvd->vg_name))) {
		log_error("Volume Group name allocation failed.");
		return 0;
	}

	memcpy(&pv->vgid, vgd->vg_uuid, sizeof(vg->id));

	/* Store system_id from first PV if PV belongs to a VG */
	if (vg && !*vg->lvm1_system_id)
		strncpy(vg->lvm1_system_id, (char *)pvd->system_id, NAME_LEN);

	if (vg &&
	    strncmp(vg->lvm1_system_id, (char *)pvd->system_id, sizeof(pvd->system_id)))
		    log_very_verbose("System ID %s on %s differs from %s for "
				     "volume group", pvd->system_id,
				     pv_dev_name(pv), vg->lvm1_system_id);

	/*
	 * If exported, we still need to flag in pv->status too because
	 * we don't always have a struct volume_group when we need this.
	 */
	if (pvd->pv_status & VG_EXPORTED)
		pv->status |= EXPORTED_VG;

	if (pvd->pv_allocatable)
		pv->status |= ALLOCATABLE_PV;

	pv->size = pvd->pv_size;
	pv->pe_size = pvd->pe_size;
	pv->pe_start = pvd->pe_start;
	pv->pe_count = pvd->pe_total;
	pv->pe_alloc_count = 0;
	pv->pe_align = 0;
        pv->is_labelled = 0; /* format1 PVs have no label */
        pv->label_sector = 0;

	/* Fix up pv size if missing or impossibly large */
	if (!pv->size || pv->size > (1ULL << 62)) {
		if (!dev_get_size(dev, &pv->size)) {
			log_error("%s: Couldn't get size.", pv_dev_name(pv));
			return 0;
		}
		log_verbose("Fixing up missing format1 size (%s) "
			    "for PV %s", display_size(fmt->cmd, pv->size),
			    pv_dev_name(pv));
		if (vg) {
			size = pv->pe_count * (uint64_t) vg->extent_size +
			       pv->pe_start;
			if (size > pv->size)
				log_warn("WARNING: Physical Volume %s is too "
					 "large for underlying device",
					 pv_dev_name(pv));
		}
	}

	dm_list_init(&pv->tags);
	dm_list_init(&pv->segments);

	if (!alloc_pv_segment_whole_pv(mem, pv))
		return_0;

	return 1;
}
Exemplo n.º 21
0
int dm_get_status_mirror(struct dm_pool *mem, const char *params,
			 struct dm_status_mirror **status)
{
	struct dm_status_mirror *s;
	const char *p, *pos = params;
	unsigned num_devs, argc, i;
	int used;

	if (!(s = dm_pool_zalloc(mem, sizeof(*s)))) {
		log_error("Failed to alloc mem pool to parse mirror status.");
		return 0;
	}

	if (sscanf(pos, "%u %n", &num_devs, &used) != 1)
		goto_out;
	pos += used;

	if (num_devs > DM_MIRROR_MAX_IMAGES) {
		log_error(INTERNAL_ERROR "More then " DM_TO_STRING(DM_MIRROR_MAX_IMAGES)
			  " reported in mirror status.");
		goto out;
	}

	if (!(s->devs = dm_pool_alloc(mem, num_devs * sizeof(*(s->devs))))) {
		log_error("Allocation of devs failed.");
		goto out;
	}

	for (i = 0; i < num_devs; ++i, pos += used)
		if (sscanf(pos, "%u:%u %n",
			   &(s->devs[i].major), &(s->devs[i].minor), &used) != 2)
			goto_out;

	if (sscanf(pos, FMTu64 "/" FMTu64 "%n",
		   &s->insync_regions, &s->total_regions, &used) != 2)
		goto_out;
	pos += used;

	if (sscanf(pos, "%u %n", &argc, &used) != 1)
		goto_out;
	pos += used;

	for (i = 0; i < num_devs ; ++i)
		s->devs[i].health = pos[i];

	if (!(pos = _advance_to_next_word(pos, argc)))
		goto_out;

	if (sscanf(pos, "%u %n", &argc, &used) != 1)
		goto_out;
	pos += used;

	if (argc == 1) {
		/* core, cluster-core */
		if (!(s->log_type = dm_pool_strdup(mem, pos))) {
			log_error("Allocation of log type string failed.");
			goto out;
		}
	} else {
		if (!(p = _advance_to_next_word(pos, 1)))
			goto_out;

		/* disk, cluster-disk */
		if (!(s->log_type = dm_pool_strndup(mem, pos, p - pos - 1))) {
			log_error("Allocation of log type string failed.");
			goto out;
		}
		pos = p;

		if ((argc > 2) && !strcmp(s->log_type, "disk")) {
			s->log_count = argc - 2;

			if (!(s->logs = dm_pool_alloc(mem, s->log_count * sizeof(*(s->logs))))) {
				log_error("Allocation of logs failed.");
				goto out;
			}

			for (i = 0; i < s->log_count; ++i, pos += used)
				if (sscanf(pos, "%u:%u %n",
					   &s->logs[i].major, &s->logs[i].minor, &used) != 2)
					goto_out;

			for (i = 0; i < s->log_count; ++i)
				s->logs[i].health = pos[i];
		}
	}

	s->dev_count = num_devs;
	*status = s;

	return 1;
out:
	log_error("Failed to parse mirror status %s.", params);
	dm_pool_free(mem, s);
	*status = NULL;

	return 0;
}