/* Checks that freed chunks are marked NOACCESS */
static void check_free2()
{
	struct dm_pool *p = dm_pool_create("", 900); /* 900 will get
						      * rounded up to 1024,
						      * 1024 would have got
						      * rounded up to
						      * 2048 */
	char *data1, *data2;

	assert(p);
	data1 = dm_pool_alloc(p, 123);
	assert(data1);

	data1 = dm_pool_alloc(p, 1024);
	assert(data1);

	data2 = dm_pool_alloc(p, 123);
	assert(data2);

	data2[0] = 'A';		/* should work fine */

	dm_pool_free(p, data1);

	/*
	 * so now the first chunk is active, the second chunk has become
	 * the free one.
	 */
	data2[0] = 'B';		/* should prompt an invalid write error */

	dm_pool_destroy(p);
}
/*
 * Looking at the code I'm not sure allocations that are near the chunk
 * size are working.  So this test is trying to exhibit a specific problem.
 */
static void check_allocation_near_chunk_size()
{
	int i;
	char *data;
	struct dm_pool *p = dm_pool_create("", 900);

	/*
	 * allocate a lot and then free everything so we know there
	 * is a spare chunk.
	 */
	for (i = 0; i < 1000; i++) {
		data = dm_pool_alloc(p, 37);
		memset(data, 0, 37);
		assert(data);
	}

	dm_pool_empty(p);

	/* now we allocate something close to the chunk size ... */
	data = dm_pool_alloc(p, 1020);
	assert(data);
	memset(data, 0, 1020);

	dm_pool_destroy(p);
}
Exemple #3
0
static int _read_lvs(struct disk_list *data)
{
	unsigned int i, lvs_read = 0;
	uint64_t pos;
	struct lvd_list *ll;
	struct vg_disk *vgd = &data->vgd;

	for (i = 0; (i < vgd->lv_max) && (lvs_read < vgd->lv_cur); i++) {
		pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
		ll = dm_pool_alloc(data->mem, sizeof(*ll));

		if (!ll)
			return_0;

		if (!_read_lvd(data->dev, pos, &ll->lvd))
			return_0;

		if (!_check_lvd(&ll->lvd))
			continue;

		lvs_read++;
		dm_list_add(&data->lvds, &ll->list);
	}

	return 1;
}
Exemple #4
0
static int _read_uuids(struct disk_list *data)
{
	unsigned num_read = 0;
	struct uuid_list *ul;
	char buffer[NAME_LEN] __attribute__((aligned(8)));
	uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
	uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;

	while (pos < end && num_read < data->vgd.pv_cur) {
		if (!dev_read(data->dev, pos, sizeof(buffer), buffer))
			return_0;

		if (!(ul = dm_pool_alloc(data->mem, sizeof(*ul))))
			return_0;

		memcpy(ul->uuid, buffer, NAME_LEN);
		ul->uuid[NAME_LEN - 1] = '\0';

		dm_list_add(&data->uuids, &ul->list);

		pos += NAME_LEN;
		num_read++;
	}

	return 1;
}
Exemple #5
0
struct config_tree *create_config_tree_from_string(const char *config_settings)
{
	struct cs *c;
	struct config_tree *cft;
	struct parser *p;

	if (!(cft = create_config_tree(NULL, 0)))
		return_NULL;

	c = (struct cs *) cft;
	if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
		log_error("Failed to allocate config tree parser.");
		destroy_config_tree(cft);
		return NULL;
	}

	p->mem = c->mem;
	p->fb = config_settings;
	p->fe = config_settings + strlen(config_settings);

	if (!_parse_config_file(p, cft)) {
		destroy_config_tree(cft);
		return_NULL;
	}

	return cft;
}
Exemple #6
0
static int detach_metadata_devices(struct lv_segment *seg, struct dm_list *list)
{
	uint32_t s;
	uint32_t num_meta_lvs;
	struct cmd_context *cmd = seg->lv->vg->cmd;
	struct lv_list *lvl;

	num_meta_lvs = seg_is_raid(seg) ? seg->area_count : !!seg->log_lv;

	if (!num_meta_lvs)
		return_0;

	if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl) * num_meta_lvs)))
		return_0;

	if (seg_is_raid(seg)) {
		for (s = 0; s < seg->area_count; s++) {
			if (!seg_metalv(seg, s))
				return_0; /* Trap this future possibility */

			lvl[s].lv = seg_metalv(seg, s);
			lv_set_visible(lvl[s].lv);

			dm_list_add(list, &lvl[s].list);
		}
		return 1;
	}

	lvl[0].lv = detach_mirror_log(seg);
	dm_list_add(list, &lvl[0].list);

	return 1;
}
/* FIXME: test the dbg_malloc at exit (this test should be in dbg_malloc) */
static void check_leak_detection()
{
	int i;
	struct dm_pool *p = dm_pool_create("", 1024);

	for (i = 0; i < 10; i++)
		dm_pool_alloc(p, (i + 1) * 37);
}
Exemple #8
0
static char *_dup_token(struct dm_pool *mem, const char *b, const char *e)
{
	size_t len = e - b;
	char *str = dm_pool_alloc(mem, len + 1);
	if (!str) {
		log_error("Failed to duplicate token.");
		return 0;
	}
	memcpy(str, b, len);
	str[len] = '\0';
	return str;
}
Exemple #9
0
static char *_dup_tok(struct parser *p)
{
	size_t len = p->te - p->tb;
	char *str = dm_pool_alloc(p->mem, len + 1);
	if (!str) {
		log_error("Failed to duplicate token.");
		return 0;
	}
	memcpy(str, p->tb, len);
	str[len] = '\0';
	return str;
}
Exemple #10
0
int internal_filter_allow(struct dm_pool *mem, struct device *dev)
{
	struct device_list *devl;

	if (!(devl = dm_pool_alloc(mem, sizeof(*devl)))) {
		log_error("device_list element allocation failed");
		return 0;
	}
	devl->dev = dev;

	dm_list_add(&_allow_devs, &devl->list);
	return 1;
}
Exemple #11
0
static struct mirror_state *_mirrored_init_target(struct dm_pool *mem,
                                         struct cmd_context *cmd)
{
        struct mirror_state *mirr_state;

        if (!(mirr_state = dm_pool_alloc(mem, sizeof(*mirr_state)))) {
                log_error("struct mirr_state allocation failed");
                return NULL;
        }

        mirr_state->default_region_size = 2 *
            find_config_tree_int(cmd,
                            "activation/mirror_region_size",
                            DEFAULT_MIRROR_REGION_SIZE);

        return mirr_state;
}
Exemple #12
0
static int _read_extents(struct disk_list *data)
{
	size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
	struct pe_disk *extents = dm_pool_alloc(data->mem, len);
	uint64_t pos = data->pvd.pe_on_disk.base;

	if (!extents)
		return_0;

	if (!dev_read(data->dev, pos, len, extents))
		return_0;

	_xlate_extents(extents, data->pvd.pe_total);
	data->extents = extents;

	return 1;
}
Exemple #13
0
char *dm_build_dm_uuid(struct dm_pool *mem, const char *uuid_prefix, const char *lvid, const char *layer)
{
	char *dmuuid;
	size_t len;

	if (!layer)
		layer = "";

	len = strlen(uuid_prefix) + strlen(lvid) + strlen(layer) + 2;

	if (!(dmuuid = dm_pool_alloc(mem, len))) {
		log_error("build_dm_name: Allocation failed for %" PRIsize_t
			  " %s %s.", len, lvid, layer);
		return NULL;
	}

	sprintf(dmuuid, "%s%s%s%s", uuid_prefix, lvid, (*layer) ? "-" : "", layer);

	return dmuuid;
}
Exemple #14
0
int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end)
{
	/* TODO? if (start == end) return 1; */

	struct parser *p;
	if (!(p = dm_pool_alloc(cft->mem, sizeof(*p))))
		return_0;

	p->mem = cft->mem;
	p->fb = start;
	p->fe = end;
	p->tb = p->te = p->fb;
	p->line = 1;

	_get_token(p, TOK_SECTION_E);
	if (!(cft->root = _file(p)))
		return_0;

	return 1;
}
static void check_free()
{
        int i;
        char *blocks[COUNT];
        struct dm_pool *p = dm_pool_create("blah", 1024);

        for (i = 0; i < COUNT; i++)
                blocks[i] = dm_pool_alloc(p, 37);

        /* check we can access the last block */
        blocks[COUNT - 1][0] = 'E';
        if (blocks[COUNT - 1][0] == 'E')
                printf("first branch worked (as expected)\n");

        dm_pool_free(p, blocks[5]);

        if (blocks[COUNT - 1][0] == 'E')
                printf("second branch worked (valgrind should have flagged this as an error)\n");

        dm_pool_destroy(p);
}
Exemple #16
0
/*
 * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
 */
char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
		       const char *lvname, const char *layer)
{
	size_t len = 1;
	int hyphens = 1;
	char *r, *out;

	_count_chars(vgname, &len, &hyphens, '-', 0);
	_count_chars(lvname, &len, &hyphens, '-', 0);

	if (layer && *layer) {
		_count_chars(layer, &len, &hyphens, '-', 0);
		hyphens++;
	}

	len += hyphens;

	if (!(r = dm_pool_alloc(mem, len))) {
		log_error("build_dm_name: Allocation failed for %" PRIsize_t
			  " for %s %s %s.", len, vgname, lvname, layer);
		return NULL;
	}

	out = r;
	_quote_hyphens(&out, vgname);
	*out++ = '-';
	_quote_hyphens(&out, lvname);

	if (layer && *layer) {
		/* No hyphen if the layer begins with _ e.g. _mlog */
		if (*layer != '_')
			*out++ = '-';
		_quote_hyphens(&out, layer);
	}
	*out = '\0';

	return r;
}
Exemple #17
0
int read_config_fd(struct config_tree *cft, struct device *dev,
		   off_t offset, size_t size, off_t offset2, size_t size2,
		   checksum_fn_t checksum_fn, uint32_t checksum)
{
	struct cs *c = (struct cs *) cft;
	struct parser *p;
	int r = 0;
	int use_mmap = 1;
	off_t mmap_offset = 0;
	char *buf = NULL;

	if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
		return_0;
	p->mem = c->mem;

	/* Only use mmap with regular files */
	if (!(dev->flags & DEV_REGULAR) || size2)
		use_mmap = 0;

	if (use_mmap) {
		mmap_offset = offset % lvm_getpagesize();
		/* memory map the file */
		p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
			     MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
		if (p->fb == (caddr_t) (-1)) {
			log_sys_error("mmap", dev_name(dev));
			goto out;
		}
		p->fb = p->fb + mmap_offset;
	} else {
		if (!(buf = dm_malloc(size + size2)))
			return_0;
		if (!dev_read_circular(dev, (uint64_t) offset, size,
				       (uint64_t) offset2, size2, buf)) {
			goto out;
		}
		p->fb = buf;
	}

	if (checksum_fn && checksum !=
	    (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)p->fb, size),
			 (const uint8_t *)(p->fb + size), size2))) {
		log_error("%s: Checksum error", dev_name(dev));
		goto out;
	}

	p->fe = p->fb + size + size2;

	if (!_parse_config_file(p, cft))
		goto_out;

	r = 1;

      out:
	if (!use_mmap)
		dm_free(buf);
	else {
		/* unmap the file */
		if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
			log_sys_error("munmap", dev_name(dev));
			r = 0;
		}
	}

	return r;
}
Exemple #18
0
static int _report(struct cmd_context *cmd, int argc, char **argv,
		   report_type_t report_type)
{
	void *report_handle;
	const char *opts;
	char *str;
	const char *keys = NULL, *options = NULL, *separator;
	int r = ECMD_PROCESSED;
	int aligned, buffered, headings;
	unsigned args_are_pvs;

	aligned = find_config_tree_int(cmd, "report/aligned",
				  DEFAULT_REP_ALIGNED);
	buffered = find_config_tree_int(cmd, "report/buffered",
				   DEFAULT_REP_BUFFERED);
	headings = find_config_tree_int(cmd, "report/headings",
				   DEFAULT_REP_HEADINGS);
	separator = find_config_tree_str(cmd, "report/separator",
				    DEFAULT_REP_SEPARATOR);

	args_are_pvs = (report_type == PVS || report_type == PVSEGS) ? 1 : 0;

	switch (report_type) {
	case LVS:
		keys = find_config_tree_str(cmd, "report/lvs_sort",
				       DEFAULT_LVS_SORT);
		if (!arg_count(cmd, verbose_ARG))
			options = find_config_tree_str(cmd,
						  "report/lvs_cols",
						  DEFAULT_LVS_COLS);
		else
			options = find_config_tree_str(cmd,
						  "report/lvs_cols_verbose",
						  DEFAULT_LVS_COLS_VERB);
		break;
	case VGS:
		keys = find_config_tree_str(cmd, "report/vgs_sort",
				       DEFAULT_VGS_SORT);
		if (!arg_count(cmd, verbose_ARG))
			options = find_config_tree_str(cmd,
						  "report/vgs_cols",
						  DEFAULT_VGS_COLS);
		else
			options = find_config_tree_str(cmd,
						  "report/vgs_cols_verbose",
						  DEFAULT_VGS_COLS_VERB);
		break;
	case PVS:
		keys = find_config_tree_str(cmd, "report/pvs_sort",
				       DEFAULT_PVS_SORT);
		if (!arg_count(cmd, verbose_ARG))
			options = find_config_tree_str(cmd,
						  "report/pvs_cols",
						  DEFAULT_PVS_COLS);
		else
			options = find_config_tree_str(cmd,
						  "report/pvs_cols_verbose",
						  DEFAULT_PVS_COLS_VERB);
		break;
	case SEGS:
		keys = find_config_tree_str(cmd, "report/segs_sort",
				       DEFAULT_SEGS_SORT);
		if (!arg_count(cmd, verbose_ARG))
			options = find_config_tree_str(cmd,
						  "report/segs_cols",
						  DEFAULT_SEGS_COLS);
		else
			options = find_config_tree_str(cmd,
						  "report/segs_cols_verbose",
						  DEFAULT_SEGS_COLS_VERB);
		break;
	case PVSEGS:
		keys = find_config_tree_str(cmd, "report/pvsegs_sort",
				       DEFAULT_PVSEGS_SORT);
		if (!arg_count(cmd, verbose_ARG))
			options = find_config_tree_str(cmd,
						  "report/pvsegs_cols",
						  DEFAULT_PVSEGS_COLS);
		else
			options = find_config_tree_str(cmd,
						  "report/pvsegs_cols_verbose",
						  DEFAULT_PVSEGS_COLS_VERB);
		break;
	}

	/* If -o supplied use it, else use default for report_type */
	if (arg_count(cmd, options_ARG)) {
		opts = arg_str_value(cmd, options_ARG, "");
		if (!opts || !*opts) {
			log_error("Invalid options string: %s", opts);
			return 0;
		}
		if (*opts == '+') {
			if (!(str = dm_pool_alloc(cmd->mem,
					 strlen(options) + strlen(opts) + 1))) {
				log_error("options string allocation failed");
				return 0;
			}
			strcpy(str, options);
			strcat(str, ",");
			strcat(str, opts + 1);
			options = str;
		} else
			options = opts;
	}

	/* -O overrides default sort settings */
	if (arg_count(cmd, sort_ARG))
		keys = arg_str_value(cmd, sort_ARG, "");

	if (arg_count(cmd, separator_ARG))
		separator = arg_str_value(cmd, separator_ARG, " ");
	if (arg_count(cmd, separator_ARG))
		aligned = 0;
	if (arg_count(cmd, aligned_ARG))
		aligned = 1;
	if (arg_count(cmd, unbuffered_ARG) && !arg_count(cmd, sort_ARG))
		buffered = 0;
	if (arg_count(cmd, noheadings_ARG))
		headings = 0;

	if (!(report_handle = report_init(cmd, options, keys, &report_type,
					  separator, aligned, buffered,
					  headings)))
		return_0;

	/* Ensure options selected are compatible */
	if (report_type & SEGS)
		report_type |= LVS;
	if (report_type & PVSEGS)
		report_type |= PVS;
	if ((report_type & LVS) && (report_type & PVS)) {
		log_error("Can't report LV and PV fields at the same time");
		dm_report_free(report_handle);
		return 0;
	}

	/* Change report type if fields specified makes this necessary */
	if (report_type & SEGS)
		report_type = SEGS;
	else if (report_type & LVS)
		report_type = LVS;
	else if (report_type & PVSEGS)
		report_type = PVSEGS;
	else if (report_type & PVS)
		report_type = PVS;

	switch (report_type) {
	case LVS:
		r = process_each_lv(cmd, argc, argv, LCK_VG_READ, report_handle,
				    &_lvs_single);
		break;
	case VGS:
		r = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0,
				    report_handle, &_vgs_single);
		break;
	case PVS:
		if (args_are_pvs)
			r = process_each_pv(cmd, argc, argv, NULL,
					    report_handle, &_pvs_single);
		else
			r = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0,
					    report_handle, &_pvs_in_vg);
		break;
	case SEGS:
		r = process_each_lv(cmd, argc, argv, LCK_VG_READ, report_handle,
				    &_lvsegs_single);
		break;
	case PVSEGS:
		if (args_are_pvs)
			r = process_each_pv(cmd, argc, argv, NULL,
					    report_handle, &_pvsegs_single);
		else
			r = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0,
					    report_handle, &_pvsegs_in_vg);
		break;
	}

	dm_report_output(report_handle);

	dm_report_free(report_handle);
	return r;
}
Exemple #19
0
const char *dm_size_to_string(struct dm_pool *mem, uint64_t size,
			      char unit_type, int use_si_units, 
			      uint64_t unit_factor, int include_suffix, 
			      dm_size_suffix_t suffix_type)
{
	unsigned base = BASE_UNKNOWN;
	unsigned s;
	int precision;
	uint64_t byte = UINT64_C(0);
	uint64_t units = UINT64_C(1024);
	char *size_buf = NULL;
	char new_unit_type = '\0', unit_type_buf[2];
	const char * const size_str[][3] = {
		/* BASE_UNKNOWN */
		{"         ", "   ", " "},	/* [0] */

		/* BASE_SHARED - Used if use_si_units = 0 */
		{" Exabyte", " EB", "E"},	/* [1] */
		{" Petabyte", " PB", "P"},	/* [2] */
		{" Terabyte", " TB", "T"},	/* [3] */
		{" Gigabyte", " GB", "G"},	/* [4] */
		{" Megabyte", " MB", "M"},	/* [5] */
		{" Kilobyte", " KB", "K"},	/* [6] */
		{" Byte    ", " B", "B"},	/* [7] */

		/* BASE_1024 - Used if use_si_units = 1 */
		{" Exbibyte", " EiB", "e"},	/* [8] */
		{" Pebibyte", " PiB", "p"},	/* [9] */
		{" Tebibyte", " TiB", "t"},	/* [10] */
		{" Gibibyte", " GiB", "g"},	/* [11] */
		{" Mebibyte", " MiB", "m"},	/* [12] */
		{" Kibibyte", " KiB", "k"},	/* [13] */
		{" Byte    ", " B", "b"},	/* [14] */

		/* BASE_1000 - Used if use_si_units = 1 */
		{" Exabyte",  " EB", "E"},	/* [15] */
		{" Petabyte", " PB", "P"},	/* [16] */
		{" Terabyte", " TB", "T"},	/* [17] */
		{" Gigabyte", " GB", "G"},	/* [18] */
		{" Megabyte", " MB", "M"},	/* [19] */
		{" Kilobyte", " kB", "K"},	/* [20] */

		/* BASE_SPECIAL */
		{" Byte    ", " B ", "B"},	/* [21] (shared with BASE_1000) */
		{" Units   ", " Un", "U"},	/* [22] */
		{" Sectors ", " Se", "S"},	/* [23] */
	};

	if (!(size_buf = dm_pool_alloc(mem, SIZE_BUF))) {
		log_error("no memory for size display buffer");
		return "";
	}

	if (!use_si_units) {
		/* Case-independent match */
		for (s = 0; s < NUM_UNIT_PREFIXES; s++)
			if (toupper((int) unit_type) ==
			    *size_str[BASE_SHARED + s][2]) {
				base = BASE_SHARED;
				break;
			}
	} else {
		/* Case-dependent match for powers of 1000 */
		for (s = 0; s < NUM_UNIT_PREFIXES; s++)
			if (unit_type == *size_str[BASE_1000 + s][2]) {
				base = BASE_1000;
				break;
			}

		/* Case-dependent match for powers of 1024 */
		if (base == BASE_UNKNOWN)
			for (s = 0; s < NUM_UNIT_PREFIXES; s++)
			if (unit_type == *size_str[BASE_1024 + s][2]) {
				base = BASE_1024;
				break;
			}
	}

	if (base == BASE_UNKNOWN)
		/* Check for special units - s, b or u */
		for (s = 0; s < NUM_SPECIAL; s++)
			if (toupper((int) unit_type) ==
			    *size_str[BASE_SPECIAL + s][2]) {
				base = BASE_SPECIAL;
				break;
			}

	if (size == UINT64_C(0)) {
		if (base == BASE_UNKNOWN)
			s = 0;
		sprintf(size_buf, "0%s", include_suffix ? size_str[base + s][suffix_type] : "");
		return size_buf;
	}

	size *= UINT64_C(512);

	if (base != BASE_UNKNOWN) {
		if (!unit_factor) {
			unit_type_buf[0] = unit_type;
			unit_type_buf[1] = '\0';
			if (!(unit_factor = dm_units_to_factor(&unit_type_buf[0], &new_unit_type, 1, NULL)) ||
			    unit_type != new_unit_type) {
				/* The two functions should match (and unrecognised units get treated like 'h'). */
				log_error(INTERNAL_ERROR "Inconsistent units: %c and %c.", unit_type, new_unit_type);
				return "";
			}
		}
		byte = unit_factor;
	} else {
		/* Human-readable style */
		if (unit_type == 'H') {
			units = UINT64_C(1000);
			base = BASE_1000;
		} else {
			units = UINT64_C(1024);
			base = BASE_1024;
		}

		if (!use_si_units)
			base = BASE_SHARED;

		byte = units * units * units * units * units * units;

		for (s = 0; s < NUM_UNIT_PREFIXES && size < byte; s++)
			byte /= units;

		include_suffix = 1;
	}

	/* FIXME Make precision configurable */
	switch (toupper(*size_str[base + s][DM_SIZE_UNIT])) {
	case 'B':
	case 'S':
		precision = 0;
		break;
	default:
		precision = 2;
	}

	snprintf(size_buf, SIZE_BUF - 1, "%.*f%s", precision,
		 (double) size / byte, include_suffix ? size_str[base + s][suffix_type] : "");

	return size_buf;
}
Exemple #20
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;
}