Commit message: libblkid: avoid non-empty recursion in EBR

This is extension to the patch 7164a1c34d18831ac61c6744ad14ce916d389b3f.

We also need to detect non-empty recursion in the EBR chain. It's
possible to create standard valid logical partitions and in the last one
points back to the EBR chain. In this case all offsets will be non-empty.

Unfortunately, it's valid to create logical partitions that are not in
the "disk order" (sorted by start offset). So link somewhere back is
valid, but this link cannot points to already existing partition
(otherwise we will see recursion).

This patch forces libblkid to ignore duplicate logical partitions, the
duplicate chain segment is interpreted as non-data segment, after 100
iterations with non-data segments it will break the loop -- no memory
is allocated in this case by the loop.

Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1349536
References: http://seclists.org/oss-sec/2016/q3/40
Signed-off-by: Karel Zak <kzak@redhat.com>
CVE: CVE-2016-5011
CWE: NVD-CWE-noinfo
Vulnerability description: The parse_dos_extended function in partitions/dos.c in the libblkid library in util-linux allows physically proximate attackers to cause a denial of service (memory consumption) via a crafted MSDOS partition table with an extended partition boot record at zero offset.

Project: util-linux
File: partitions.c

int blkid_probe_set_partitions_flags(blkid_probe pr, int flags)
{
	pr->chains[BLKID_CHAIN_PARTS].flags = flags;
	return 0;
}

Project: util-linux
File: partitions.c

static int partitions_probe(blkid_probe pr, struct blkid_chain *chn)
{
	int rc = BLKID_PROBE_NONE;
	size_t i;

	if (!pr || chn->idx < -1)
		return -EINVAL;

	blkid_probe_chain_reset_values(pr, chn);

	if (pr->flags & BLKID_FL_NOSCAN_DEV)
		return BLKID_PROBE_NONE;

	if (chn->binary)
		partitions_init_data(chn);

	if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT))
		goto details_only;

	DBG(LOWPROBE, ul_debug("--> starting probing loop [PARTS idx=%d]",
		chn->idx));

	i = chn->idx < 0 ? 0 : chn->idx + 1U;

	for ( ; i < ARRAY_SIZE(idinfos); i++) {
		const char *name;

		chn->idx = i;

		/* apply filter */
		if (chn->fltr && blkid_bmp_get_item(chn->fltr, i))
			continue;

		/* apply checks from idinfo */
		rc = idinfo_probe(pr, idinfos[i], chn);
		if (rc < 0)
			break;
		if (rc != BLKID_PROBE_OK)
			continue;

		name = idinfos[i]->name;

		if (!chn->binary)
			/*
			 * Non-binary interface, set generic variables. Note
			 * that the another variables could be set in prober
			 * functions.
			 */
			blkid_probe_set_value(pr, "PTTYPE",
						(unsigned char *) name,
						strlen(name) + 1);

		DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]",
			name, chn->idx));
		rc = BLKID_PROBE_OK;
		break;
	}

	if (rc != BLKID_PROBE_OK) {
		DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]",
			rc, chn->idx));
	}

details_only:
	/*
	 * Gather PART_ENTRY_* values if the current device is a partition.
	 */
	if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary &&
	    (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) {

		int xrc = blkid_partitions_probe_partition(pr);

		/* partition entry probing is optional, and "not-found" from
		 * this sub-probing must not to overwrite previous success. */
		if (xrc < 0)
			rc = xrc;			/* always propagate errors */
		else if (rc == BLKID_PROBE_NONE)
			rc = xrc;
	}

	DBG(LOWPROBE, ul_debug("partitions probe done [rc=%d]",	rc));
	return rc;
}

Project: util-linux
File: partitions.c

blkid_partlist blkid_probe_get_partitions(blkid_probe pr)
{
	return (blkid_partlist) blkid_probe_get_binary_data(pr,
			&pr->chains[BLKID_CHAIN_PARTS]);
}

Project: util-linux
File: partitions.c

blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n)
{
	int i, nparts;
	blkid_partition par;

	nparts = blkid_partlist_numof_partitions(ls);
	for (i = 0; i < nparts; i++) {
		par = blkid_partlist_get_partition(ls, i);
		if (n == blkid_partition_get_partno(par))
			return par;
	}
	return NULL;
}

Project: util-linux
File: partitions.c

blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab)
{
	return (blkid_loff_t)tab->offset;
}

Project: util-linux
File: partitions.c

static void free_parttables(blkid_partlist ls)
{
	if (!ls || !ls->l_tabs.next)
		return;

	/* remove unassigned partition tables */
	while (!list_empty(&ls->l_tabs)) {
		blkid_parttable tab = list_entry(ls->l_tabs.next,
					struct blkid_struct_parttable, t_tabs);
		unref_parttable(tab);
	}
}

Project: util-linux
File: partitions.c

int blkid_partition_is_primary(blkid_partition par)
{
	return partition_get_logical_type(par) == 'P' ? TRUE : FALSE;
}

Project: util-linux
File: partitions.c

int blkid_partition_is_logical(blkid_partition par)
{
	return partition_get_logical_type(par) == 'L' ? TRUE : FALSE;
}

Project: util-linux
File: partitions.c

const char *blkid_parttable_get_id(blkid_parttable tab)
{
	return *tab->id ? tab->id : NULL;
}

Project: util-linux
File: partitions.c

int blkid_partition_set_type_string(blkid_partition par,
		const unsigned char *type, size_t len)
{
	set_string((unsigned char *) par->typestr,
			sizeof(par->typestr), type, len);
	return 0;
}

Project: util-linux
File: partitions.c

static blkid_partlist partitions_init_data(struct blkid_chain *chn)
{
	blkid_partlist ls;

	if (chn->data)
		ls = (blkid_partlist) chn->data;
	else {
		/* allocate the new list of partitions */
		ls = calloc(1, sizeof(struct blkid_struct_partlist));
		if (!ls)
			return NULL;
		chn->data = (void *) ls;
	}

	reset_partlist(ls);

	DBG(LOWPROBE, ul_debug("parts: initialized partitions list (%p, size=%d)",
		ls, ls->nparts_max));
	return ls;
}

Project: util-linux
File: partitions.c

int blkid_partition_get_partno(blkid_partition par)
{
	return par->partno;
}

Project: util-linux
File: partitions.c

int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent,
		const struct blkid_idinfo *id)
{
	blkid_probe prc;
	int rc;
	blkid_partlist ls;
	uint64_t sz, off;

	DBG(LOWPROBE, ul_debug(
		"parts: ----> %s subprobe requested (parent=%p)",
		id->name, parent));

	if (!pr || !parent || !parent->size)
		return -EINVAL;
	if (pr->flags & BLKID_FL_NOSCAN_DEV)
		return BLKID_PROBE_NONE;

	/* range defined by parent */
	sz = parent->size << 9;
	off = parent->start << 9;

	if (off < pr->off || pr->off + pr->size < off + sz) {
		DBG(LOWPROBE, ul_debug(
			"ERROR: parts: <---- '%s' subprobe: overflow detected.",
			id->name));
		return -ENOSPC;
	}

	/* create private prober */
	prc = blkid_clone_probe(pr);
	if (!prc)
		return -ENOMEM;

	blkid_probe_set_dimension(prc, off, sz);

	/* clone is always with reset chain, fix it */
	prc->cur_chain = blkid_probe_get_chain(pr);

	/*
	 * Set 'parent' to the current list of the partitions and use the list
	 * in cloned prober (so the cloned prober will extend the current list
	 * of partitions rather than create a new).
	 */
	ls = blkid_probe_get_partlist(pr);
	blkid_partlist_set_parent(ls, parent);

	blkid_probe_set_partlist(prc, ls);

	rc = idinfo_probe(prc, id, blkid_probe_get_chain(pr));

	blkid_probe_set_partlist(prc, NULL);
	blkid_partlist_set_parent(ls, NULL);

	blkid_free_probe(prc);	/* free cloned prober */

	DBG(LOWPROBE, ul_debug(
		"parts: <---- %s subprobe done (parent=%p, rc=%d)",
		id->name, parent, rc));

	return rc;
}

Project: util-linux
File: partitions.c

int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id)
{
	if (!tab)
		return -1;

	blkid_unparse_uuid(id, tab->id, sizeof(tab->id));
	return 0;
}

Project: util-linux
File: partitions.c

int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid)
{
	blkid_unparse_uuid(uuid, par->typestr, sizeof(par->typestr));
	return 0;
}

Project: util-linux
File: partitions.c

int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[])
{
	return __blkid_probe_filter_types(pr, BLKID_CHAIN_PARTS, flag, names);
}

Project: util-linux
File: partitions.c

int blkid_known_pttype(const char *pttype)
{
	size_t i;

	if (!pttype)
		return 0;

	for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
		const struct blkid_idinfo *id = idinfos[i];
		if (strcmp(id->name, pttype) == 0)
			return 1;
	}
	return 0;
}

Project: util-linux
File: partitions.c

int blkid_partitions_strcpy_ptuuid(blkid_probe pr, char *str)
{
	struct blkid_chain *chn = blkid_probe_get_chain(pr);

	if (chn->binary || !str || !*str)
		return 0;

	if (!blkid_probe_set_value(pr, "PTUUID", (unsigned char *) str, strlen(str) + 1))
		return -ENOMEM;

	return 0;
}

Project: util-linux
File: partitions.c

const char *blkid_partition_get_name(blkid_partition par)
{
	return *par->name ? (char *) par->name : NULL;
}

Project: util-linux
File: partitions.c

int blkid_partition_is_extended(blkid_partition par)
{
	return partition_get_logical_type(par) == 'E' ? TRUE : FALSE;
}

Project: util-linux
File: partitions.c

blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
				const char *type, uint64_t offset)
{
	blkid_parttable tab;

	tab = calloc(1, sizeof(struct blkid_struct_parttable));
	if (!tab)
		return NULL;
	tab->type = type;
	tab->offset = offset;
	tab->parent = ls->next_parent;

	INIT_LIST_HEAD(&tab->t_tabs);
	list_add_tail(&tab->t_tabs, &ls->l_tabs);

	DBG(LOWPROBE, ul_debug("parts: create a new partition table "
		       "(%p, type=%s, offset=%"PRId64")", tab, type, offset));
	return tab;
}

Project: util-linux
File: partitions.c

static void blkid_probe_set_partlist(blkid_probe pr, blkid_partlist ls)
{
	pr->chains[BLKID_CHAIN_PARTS].data = ls;
}

Project: util-linux
File: partitions.c

blkid_parttable blkid_partition_get_table(blkid_partition par)
{
	return par->tab;
}

Project: util-linux
File: partitions.c

unsigned long long blkid_partition_get_flags(blkid_partition par)
{
	return par->flags;
}

Project: util-linux
File: partitions.c

int blkid_partlist_numof_partitions(blkid_partlist ls)
{
	return ls->nparts;
}

Project: util-linux
File: partitions.c

blkid_loff_t blkid_partition_get_size(blkid_partition par)
{
	return (blkid_loff_t)par->size;
}

Project: util-linux
File: partitions.c

static void set_string(unsigned char *item, size_t max,
				const unsigned char *data, size_t len)
{
	if (len >= max)
		len = max - 1;

	memcpy(item, data, len);
	item[len] = '\0';

	blkid_rtrim_whitespace(item);
}

Project: util-linux
File: partitions.c

blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
{
	if (list_empty(&ls->l_tabs))
		return NULL;

	return list_entry(ls->l_tabs.next,
			struct blkid_struct_parttable, t_tabs);
}

Project: util-linux
File: partitions.c

const char *blkid_parttable_get_type(blkid_parttable tab)
{
	return tab->type;
}

Project: util-linux
File: partitions.c

int blkid_partition_set_type(blkid_partition par, int type)
{
	par->type = type;
	return 0;
}

Project: util-linux
File: partitions.c

static int partition_get_logical_type(blkid_partition par)
{
	blkid_parttable tab;

	if (!par)
		return -1;

	tab = blkid_partition_get_table(par);
	if (!tab || !tab->type)
		return -1;

	if (tab->parent)
		return 'L';  /* report nested partitions as logical */

	if (!strcmp(tab->type, "dos")) {
		if (par->partno > 4)
			return 'L';	/* logical */

	        if(par->type == MBR_DOS_EXTENDED_PARTITION ||
                   par->type == MBR_W95_EXTENDED_PARTITION ||
		   par->type == MBR_LINUX_EXTENDED_PARTITION)
			return 'E';
	}
	return 'P';
}

Project: util-linux
File: partitions.c

int blkid_partitions_need_typeonly(blkid_probe pr)
{
	struct blkid_chain *chn = blkid_probe_get_chain(pr);

	return chn && chn->data && chn->binary ? FALSE : TRUE;
}

Project: util-linux
File: partitions.c

blkid_partition blkid_partlist_get_parent(blkid_partlist ls)
{
	if (!ls)
		return NULL;
	return ls->next_parent;
}

Project: util-linux
File: partitions.c

blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
{
	return (blkid_partlist) pr->chains[BLKID_CHAIN_PARTS].data;
}

Project: util-linux
File: partitions.c

int blkid_partitions_get_flags(blkid_probe pr)
{
	struct blkid_chain *chn = blkid_probe_get_chain(pr);

	return chn ? chn->flags : 0;
}

Project: util-linux
File: partitions.c

blkid_loff_t blkid_partition_get_start(blkid_partition par)
{
	return (blkid_loff_t)par->start;
}

Project: util-linux
File: partitions.c

int blkid_probe_is_covered_by_pt(blkid_probe pr,
				 uint64_t offset, uint64_t size)
{
	blkid_probe prc = NULL;
	blkid_partlist ls = NULL;
	uint64_t start, end;
	int nparts, i, rc = 0;

	DBG(LOWPROBE, ul_debug(
		"=> checking if off=%"PRIu64" size=%"PRIu64" covered by PT",
		offset, size));

	if (pr->flags & BLKID_FL_NOSCAN_DEV)
		goto done;

	prc = blkid_clone_probe(pr);
	if (!prc)
		goto done;

	ls = blkid_probe_get_partitions(prc);
	if (!ls)
		goto done;

	nparts = blkid_partlist_numof_partitions(ls);
	if (!nparts)
		goto done;

	end = (offset + size) >> 9;
	start = offset >> 9;

	/* check if the partition table fits into the device */
	for (i = 0; i < nparts; i++) {
		blkid_partition par = &ls->parts[i];

		if (par->start + par->size > (pr->size >> 9)) {
			DBG(LOWPROBE, ul_debug("partition #%d overflows "
				"device (off=%" PRId64 " size=%" PRId64 ")",
				par->partno, par->start, par->size));
			goto done;
		}
	}

	/* check if the requested area is covered by PT */
	for (i = 0; i < nparts; i++) {
		blkid_partition par = &ls->parts[i];

		if (start >= par->start && end <= par->start + par->size) {
			rc = 1;
			break;
		}
	}
done:
	blkid_free_probe(prc);

	DBG(LOWPROBE, ul_debug("<= %s covered by PT", rc ? "IS" : "NOT"));
	return rc;
}

Project: util-linux
File: partitions.c

int blkid_probe_enable_partitions(blkid_probe pr, int enable)
{
	pr->chains[BLKID_CHAIN_PARTS].enabled = enable;
	return 0;
}

Project: util-linux
File: partitions.c

int blkid_partlist_increment_partno(blkid_partlist ls)
{
	return ls ? ls->next_partno++ : -1;
}

Project: util-linux
File: partitions.c

int blkid_partition_set_name(blkid_partition par,
		const unsigned char *name, size_t len)
{
	if (!par)
		return -1;

	set_string(par->name, sizeof(par->name), name, len);
	return 0;
}

Project: util-linux
File: partitions.c

const char *blkid_partition_get_type_string(blkid_partition par)
{
	return *par->typestr ? par->typestr : NULL;
}

Project: util-linux
File: partitions.c

static void partitions_free_data(blkid_probe pr __attribute__((__unused__)),
				 void *data)
{
	blkid_partlist ls = (blkid_partlist) data;

	if (!ls)
		return;

	free_parttables(ls);

	/* deallocate partitions and partlist */
	free(ls->parts);
	free(ls);
}

Project: util-linux
File: partitions.c

int blkid_partlist_set_partno(blkid_partlist ls, int partno)
{
	if (!ls)
		return -1;
	ls->next_partno = partno;
	return 0;
}

Project: util-linux
File: dos.c

static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
		uint32_t ex_start, uint32_t ex_size, int ssf)
{
	blkid_partlist ls = blkid_probe_get_partlist(pr);
	uint32_t cur_start = ex_start, cur_size = ex_size;
	unsigned char *data;
	int ct_nodata = 0;	/* count ext.partitions without data partitions */
	int i;

	DBG(LOWPROBE, ul_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf));
	if (ex_start == 0) {
		DBG(LOWPROBE, ul_debug("Bad offset in primary extended partition -- ignore"));
		return 0;
	}

	while (1) {
		struct dos_partition *p, *p0;
		uint32_t start, size;

		if (++ct_nodata > 100)
			return BLKID_PROBE_OK;
		data = blkid_probe_get_sector(pr, cur_start);
		if (!data) {
			if (errno)
				return -errno;
			goto leave;	/* malformed partition? */
		}

		if (!mbr_is_valid_magic(data))
			goto leave;

		p0 = mbr_get_partition(data, 0);

		/* Usually, the first entry is the real data partition,
		 * the 2nd entry is the next extended partition, or empty,
		 * and the 3rd and 4th entries are unused.
		 * However, DRDOS sometimes has the extended partition as
		 * the first entry (when the data partition is empty),
		 * and OS/2 seems to use all four entries.
		 * -- Linux kernel fs/partitions/dos.c
		 *
		 * See also http://en.wikipedia.org/wiki/Extended_boot_record
		 */

		/* Parse data partition */
		for (p = p0, i = 0; i < 4; i++, p++) {
			uint32_t abs_start;
			blkid_partition par;

			/* the start is relative to the parental ext.partition */
			start = dos_partition_get_start(p) * ssf;
			size = dos_partition_get_size(p) * ssf;
			abs_start = cur_start + start;	/* absolute start */

			if (!size || is_extended(p))
				continue;
			if (i >= 2) {
				/* extra checks to detect real data on
				 * 3rd and 4th entries */
				if (start + size > cur_size)
					continue;
				if (abs_start < ex_start)
					continue;
				if (abs_start + size > ex_start + ex_size)
					continue;
			}

			/* Avoid recursive non-empty links, see ct_nodata counter */
			if (blkid_partlist_get_partition_by_start(ls, abs_start)) {
				DBG(LOWPROBE, ul_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore",
							i + 1, abs_start));
				continue;
			}

			par = blkid_partlist_add_partition(ls, tab, abs_start, size);
			if (!par)
				return -ENOMEM;

			blkid_partition_set_type(par, p->sys_ind);
			blkid_partition_set_flags(par, p->boot_ind);
			blkid_partition_gen_uuid(par);
			ct_nodata = 0;
		}
		/* The first nested ext.partition should be a link to the next
		 * logical partition. Everything other (recursive ext.partitions)
		 * is junk.
		 */
		for (p = p0, i = 0; i < 4; i++, p++) {
			start = dos_partition_get_start(p) * ssf;
			size = dos_partition_get_size(p) * ssf;

			if (size && is_extended(p)) {
				if (start == 0)
					DBG(LOWPROBE, ul_debug("#%d: EBR link offset is zero -- ignore", i + 1));
				else
					break;
			}
		}
		if (i == 4)
			goto leave;

		cur_start = ex_start + start;
		cur_size = size;
	}
leave:
	return BLKID_PROBE_OK;
}

Project: util-linux
File: partitions.c

static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab)
{
	blkid_partition par;

	if (ls->nparts + 1 > ls->nparts_max) {
		/* Linux kernel has DISK_MAX_PARTS=256, but it's too much for
		 * generic Linux machine -- let start with 32 partitions.
		 */
		void *tmp = realloc(ls->parts, (ls->nparts_max + 32) *
					sizeof(struct blkid_struct_partition));
		if (!tmp)
			return NULL;
		ls->parts = tmp;
		ls->nparts_max += 32;
	}

	par = &ls->parts[ls->nparts++];
	memset(par, 0, sizeof(struct blkid_struct_partition));

	ref_parttable(tab);
	par->tab = tab;
	par->partno = blkid_partlist_increment_partno(ls);

	return par;
}

Project: util-linux
File: partitions.c

static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id,
			struct blkid_chain *chn)
{
	const struct blkid_idmag *mag = NULL;
	uint64_t off;
	int rc = BLKID_PROBE_NONE;		/* default is nothing */

	if (pr->size <= 0 || (id->minsz && (unsigned)id->minsz > pr->size))
		goto nothing;	/* the device is too small */
	if (pr->flags & BLKID_FL_NOSCAN_DEV)
		goto nothing;

	rc = blkid_probe_get_idmag(pr, id, &off, &mag);
	if (rc != BLKID_PROBE_OK)
		goto nothing;

	/* final check by probing function */
	if (id->probefunc) {
		DBG(LOWPROBE, ul_debug(
			"%s: ---> call probefunc()", id->name));
		rc = id->probefunc(pr, mag);
		if (rc < 0) {
			/* reset after error */
			reset_partlist(blkid_probe_get_partlist(pr));
			if (chn && !chn->binary)
				blkid_probe_chain_reset_values(pr, chn);
			DBG(LOWPROBE, ul_debug("%s probefunc failed, rc %d",
						  id->name, rc));
		}
		if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary)
			rc = blkid_probe_set_magic(pr, off, mag->len,
					(unsigned char *) mag->magic);

		DBG(LOWPROBE, ul_debug("%s: <--- (rc = %d)", id->name, rc));
	}

	return rc;

nothing:
	return BLKID_PROBE_NONE;
}

Project: util-linux
File: partitions.c

int blkid_partition_set_utf8name(blkid_partition par, const unsigned char *name,
		size_t len, int enc)
{
	if (!par)
		return -1;

	blkid_encode_to_utf8(enc, par->name, sizeof(par->name), name, len);
	blkid_rtrim_whitespace(par->name);
	return 0;
}

Project: util-linux
File: partitions.c

int blkid_partition_get_type(blkid_partition par)
{
	return par->type;
}

Project: util-linux
File: partitions.c

blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
{
	if (n < 0 || n >= ls->nparts)
		return NULL;

	return &ls->parts[n];
}

Project: util-linux
File: partitions.c

int blkid_partition_set_flags(blkid_partition par, unsigned long long flags)
{
	par->flags = flags;
	return 0;
}

Project: util-linux
File: partitions.c

blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start)
{
	int i, nparts;
	blkid_partition par;

	nparts = blkid_partlist_numof_partitions(ls);
	for (i = 0; i < nparts; i++) {
		par = blkid_partlist_get_partition(ls, i);
		if ((uint64_t) blkid_partition_get_start(par) == start)
			return par;
	}
	return NULL;
}

Project: util-linux
File: partitions.c

int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id)
{
	if (!tab)
		return -1;

	strncpy(tab->id, (const char *) id, sizeof(tab->id));
	return 0;
}

Project: util-linux
File: partitions.c

static void reset_partlist(blkid_partlist ls)
{
	if (!ls)
		return;

	free_parttables(ls);

	if (ls->next_partno) {
		/* already initialized - reset */
		int tmp_nparts = ls->nparts_max;
		blkid_partition tmp_parts = ls->parts;

		memset(ls, 0, sizeof(struct blkid_struct_partlist));

		ls->nparts_max = tmp_nparts;
		ls->parts = tmp_parts;
	}

	ls->nparts = 0;
	ls->next_partno = 1;
	INIT_LIST_HEAD(&ls->l_tabs);

	DBG(LOWPROBE, ul_debug("partlist reset"));
}

Project: util-linux
File: partitions.c

static void ref_parttable(blkid_parttable tab)
{
	tab->nparts++;
}

Project: util-linux
File: partitions.c

static void unref_parttable(blkid_parttable tab)
{
	tab->nparts--;

	if (tab->nparts <= 0) {
		list_del(&tab->t_tabs);
		free(tab);
	}
}

Project: util-linux
File: partitions.c

int blkid_probe_invert_partitions_filter(blkid_probe pr)
{
	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_PARTS);
}

Project: util-linux
File: partitions.c

int blkid_is_nested_dimension(blkid_partition par,
			uint64_t start, uint64_t size)
{
	uint64_t pstart;
	uint64_t psize;

	if (!par)
		return 0;

	pstart = blkid_partition_get_start(par);
	psize = blkid_partition_get_size(par);

	if (start < pstart || start + size > pstart + psize)
		return 0;

	return 1;
}

Project: util-linux
File: partitions.c

int blkid_partition_set_uuid(blkid_partition par, const unsigned char *uuid)
{
	if (!par)
		return -1;

	blkid_unparse_uuid(uuid, par->uuid, sizeof(par->uuid));
	return 0;
}

Project: util-linux
File: partitions.c

int blkid_partition_gen_uuid(blkid_partition par)
{
	if (!par || !par->tab || !*par->tab->id)
		return -1;

	snprintf(par->uuid, sizeof(par->uuid), "%s-%02x",
			par->tab->id, par->partno);
	return 0;
}

Project: util-linux
File: partitions.c

static int blkid_partlist_set_parent(blkid_partlist ls, blkid_partition par)
{
	if (!ls)
		return -1;
	ls->next_parent = par;
	return 0;
}

Project: util-linux
File: partitions.c

const char *blkid_partition_get_uuid(blkid_partition par)
{
	return *par->uuid ? par->uuid : NULL;
}

Project: util-linux
File: partitions.c

blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
{
	return tab->parent;
}

Project: util-linux
File: partitions.c

int blkid_probe_reset_partitions_filter(blkid_probe pr)
{
	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_PARTS);
}

Project: util-linux
File: partitions.c

int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid)
{
	struct blkid_chain *chn = blkid_probe_get_chain(pr);
	struct blkid_prval *v;

	if (chn->binary || blkid_uuid_is_empty(uuid, 16))
		return 0;

	v = blkid_probe_assign_value(pr, "PTUUID");
	if (!v)
		return -ENOMEM;

	v->len = 37;
	v->data = calloc(1, v->len);
	if (v->data) {
		blkid_unparse_uuid(uuid, (char *) v->data, v->len);
		return 0;
	}

	blkid_probe_free_value(v);
	return -ENOMEM;
}

Project: util-linux
File: partitions.c

blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno)
{
	struct sysfs_cxt sysfs;
	uint64_t start, size;
	int i, rc, partno = 0;

	DBG(LOWPROBE, ul_debug("trying to convert devno 0x%llx to partition",
			(long long) devno));

	if (sysfs_init(&sysfs, devno, NULL)) {
		DBG(LOWPROBE, ul_debug("failed t init sysfs context"));
		return NULL;
	}
	rc = sysfs_read_u64(&sysfs, "size", &size);
	if (!rc) {
		rc = sysfs_read_u64(&sysfs, "start", &start);
		if (rc) {
			/* try to get partition number from DM uuid.
			 */
			char *uuid = sysfs_strdup(&sysfs, "dm/uuid");
			char *tmp = uuid;
			char *prefix = uuid ? strsep(&tmp, "-") : NULL;

			if (prefix && strncasecmp(prefix, "part", 4) == 0) {
				char *end = NULL;

				partno = strtol(prefix + 4, &end, 10);
				if (prefix == end || (end && *end))
					partno = 0;
				else
					rc = 0;		/* success */
			}
			free(uuid);
		}
	}

	sysfs_deinit(&sysfs);

	if (rc)
		return NULL;

	if (partno) {
		DBG(LOWPROBE, ul_debug("mapped by DM, using partno %d", partno));

		/*
		 * Partition mapped by kpartx does not provide "start" offset
		 * in /sys, but if we know partno and size of the partition
		 * that we can probably make the relation between the device
		 * and an entry in partition table.
		 */
		 for (i = 0; i < ls->nparts; i++) {
			 blkid_partition par = &ls->parts[i];

			 if (partno != blkid_partition_get_partno(par))
				 continue;

			 if (size == (uint64_t)blkid_partition_get_size(par) ||
			     (blkid_partition_is_extended(par) && size <= 1024ULL))
				 return par;

		 }
		 return NULL;
	}

	DBG(LOWPROBE, ul_debug("searching by offset/size"));

	for (i = 0; i < ls->nparts; i++) {
		blkid_partition par = &ls->parts[i];

		if ((uint64_t)blkid_partition_get_start(par) == start &&
		    (uint64_t)blkid_partition_get_size(par) == size)
			return par;

		/* exception for extended dos partitions */
		if ((uint64_t)blkid_partition_get_start(par) == start &&
		    blkid_partition_is_extended(par) && size <= 1024ULL)
			return par;

	}

	DBG(LOWPROBE, ul_debug("not found partition for device"));
	return NULL;
}

Project: util-linux
File: partitions.c

static int blkid_partitions_probe_partition(blkid_probe pr)
{
	blkid_probe disk_pr = NULL;
	blkid_partlist ls;
	blkid_partition par;
	dev_t devno;

	DBG(LOWPROBE, ul_debug("parts: start probing for partition entry"));

	if (pr->flags & BLKID_FL_NOSCAN_DEV)
		goto nothing;

	devno = blkid_probe_get_devno(pr);
	if (!devno)
		goto nothing;

	disk_pr = blkid_probe_get_wholedisk_probe(pr);
	if (!disk_pr)
		goto nothing;

	/* parse PT */
	ls = blkid_probe_get_partitions(disk_pr);
	if (!ls)
		goto nothing;

	par = blkid_partlist_devno_to_partition(ls, devno);
	if (!par)
		goto nothing;
	else {
		const char *v;
		blkid_parttable tab = blkid_partition_get_table(par);
		dev_t disk = blkid_probe_get_devno(disk_pr);

		if (tab) {
			v = blkid_parttable_get_type(tab);
			if (v)
				blkid_probe_set_value(pr, "PART_ENTRY_SCHEME",
					(unsigned char *) v, strlen(v) + 1);
		}

		v = blkid_partition_get_name(par);
		if (v)
			blkid_probe_set_value(pr, "PART_ENTRY_NAME",
				(unsigned char *) v, strlen(v) + 1);

		v = blkid_partition_get_uuid(par);
		if (v)
			blkid_probe_set_value(pr, "PART_ENTRY_UUID",
				(unsigned char *) v, strlen(v) + 1);

		/* type */
		v = blkid_partition_get_type_string(par);
		if (v)
			blkid_probe_set_value(pr, "PART_ENTRY_TYPE",
				(unsigned char *) v, strlen(v) + 1);
		else
			blkid_probe_sprintf_value(pr, "PART_ENTRY_TYPE",
				"0x%x", blkid_partition_get_type(par));

		if (blkid_partition_get_flags(par))
			blkid_probe_sprintf_value(pr, "PART_ENTRY_FLAGS",
				"0x%llx", blkid_partition_get_flags(par));

		blkid_probe_sprintf_value(pr, "PART_ENTRY_NUMBER",
				"%d", blkid_partition_get_partno(par));

		blkid_probe_sprintf_value(pr, "PART_ENTRY_OFFSET", "%jd",
				(intmax_t)blkid_partition_get_start(par));
		blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd",
				(intmax_t)blkid_partition_get_size(par));

		blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u",
				major(disk), minor(disk));
	}

	DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [success]"));
	return BLKID_PROBE_OK;

nothing:
	DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [nothing]"));
	return BLKID_PROBE_NONE;


}

Project: util-linux
File: partitions.c

blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
					blkid_parttable tab, uint64_t start, uint64_t size)
{
	blkid_partition par = new_partition(ls, tab);

	if (!par)
		return NULL;

	par->start = start;
	par->size = size;

	DBG(LOWPROBE, ul_debug("parts: add partition (%p start=%"
		PRIu64 ", size=%" PRIu64 ", table=%p)",
		par, par->start, par->size, tab));
	return par;
}