From 40c9c3a6b7e6c4cda627d76edb14e92e823451c3 Mon Sep 17 00:00:00 2001
From: Karel Zak
Date: Thu, 12 May 2016 14:37:29 +0200
Subject: libfdisk: add API for work with labelitems

Signed-off-by: Karel Zak <kzak@redhat.com>
---
 libfdisk/docs/libfdisk-sections.txt |  31 +++--
 libfdisk/src/Makemodule.am          |   9 +-
 libfdisk/src/fdiskP.h               |   4 +-
 libfdisk/src/init.c                 |   1 +
 libfdisk/src/item.c                 | 243 ++++++++++++++++++++++++++++++++++++
 libfdisk/src/label.c                |  11 +-
 libfdisk/src/libfdisk.h.in          |  14 ++-
 libfdisk/src/libfdisk.sym           |  10 ++
 8 files changed, 309 insertions(+), 14 deletions(-)
 create mode 100644 libfdisk/src/item.c

(limited to 'libfdisk')

diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt
index 14d770b7e..7ae7d9a96 100644
--- a/libfdisk/docs/libfdisk-sections.txt
+++ b/libfdisk/docs/libfdisk-sections.txt
@@ -91,13 +91,6 @@ fdisk_get_nlabels
 fdisk_next_label
 fdisk_get_npartitions
 <SUBSECTION>
-fdisk_field
-fdisk_field_get_id
-fdisk_field_get_name
-fdisk_field_get_width
-fdisk_field_is_number
-fdisk_fieldtype
-<SUBSECTION>
 fdisk_label
 fdisk_is_label
 fdisk_label_get_field
@@ -347,3 +340,27 @@ fdisk_iter_get_direction
 fdisk_new_iter
 fdisk_reset_iter
 </SECTION>
+
+<SECTION>
+<FILE>item</FILE>
+fdisk_new_labelitem
+fdisk_ref_labelitem
+fdisk_reset_labelitem
+fdisk_unref_labelitem
+fdisk_labelitem_get_name
+fdisk_labelitem_get_id
+fdisk_labelitem_get_data_u64
+fdisk_labelitem_get_data_string
+fdisk_labelitem_is_string
+fdisk_labelitem_is_number
+</SECTION>
+
+<SECTION>
+<FILE>field</FILE>
+fdisk_field
+fdisk_field_get_id
+fdisk_field_get_name
+fdisk_field_get_width
+fdisk_field_is_number
+fdisk_fieldtype
+</SECTION>
diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am
index e24ae5620..e3baf9038 100644
--- a/libfdisk/src/Makemodule.am
+++ b/libfdisk/src/Makemodule.am
@@ -10,6 +10,7 @@ libfdisk_la_SOURCES = \
 	libfdisk/src/fdiskP.h \
 	libfdisk/src/init.c \
 	libfdisk/src/field.c \
+	libfdisk/src/item.c \
 	libfdisk/src/test.c \
 	libfdisk/src/ask.c \
 	libfdisk/src/alignment.c \
@@ -72,7 +73,8 @@ check_PROGRAMS += \
 	test_fdisk_gpt \
 	test_fdisk_script \
 	test_fdisk_utils \
-	test_fdisk_version 
+	test_fdisk_version \
+	test_fdisk_item
 
 libfdisk_tests_cflags  = -DTEST_PROGRAM $(libfdisk_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS)
 libfdisk_tests_ldflags = libuuid.la -static
@@ -107,6 +109,11 @@ test_fdisk_version_CFLAGS = $(libfdisk_tests_cflags)
 test_fdisk_version_LDFLAGS = $(libfdisk_tests_ldflags)
 test_fdisk_version_LDADD = $(libfdisk_tests_ldadd)
 
+test_fdisk_item_SOURCES = libfdisk/src/item.c
+test_fdisk_item_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_item_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_item_LDADD = $(libfdisk_tests_ldadd)
+
 endif # BUILD_LIBFDISK_TESTS
 
 
diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h
index 6444c3d3e..e20723e61 100644
--- a/libfdisk/src/fdiskP.h
+++ b/libfdisk/src/fdiskP.h
@@ -39,6 +39,7 @@
 #define LIBFDISK_DEBUG_TAB	(1 << 8)
 #define LIBFDISK_DEBUG_SCRIPT	(1 << 9)
 #define LIBFDISK_DEBUG_WIPE	(1 << 10)
+#define LIBFDISK_DEBUG_ITEM	(1 << 11)
 #define LIBFDISK_DEBUG_ALL	0xFFFF
 
 UL_DEBUG_DECLARE_MASK(libfdisk);
@@ -420,9 +421,10 @@ extern int fdisk_probe_labels(struct fdisk_context *cxt);
 extern void fdisk_deinit_label(struct fdisk_label *lb);
 
 struct fdisk_labelitem {
+	int		refcount;	/* reference counter */
 	int		id;		/* <label>_ITEM_* */
 	char		type;		/* s = string, j = uint64 */
-	const char	*name;
+	const char	*name;		/* human readable name */
 
 	union {
 		char		*str;
diff --git a/libfdisk/src/init.c b/libfdisk/src/init.c
index b4de57cd0..2bb25c700 100644
--- a/libfdisk/src/init.c
+++ b/libfdisk/src/init.c
@@ -22,6 +22,7 @@ UL_DEBUG_DEFINE_MASKNAMES(libfdisk) =
 	{ "script",	LIBFDISK_DEBUG_SCRIPT,	"sfdisk-like scripts" },
 	{ "tab",	LIBFDISK_DEBUG_TAB,	"table utils"},
 	{ "wipe",       LIBFDISK_DEBUG_WIPE,    "wipe area utils" },
+	{ "item",       LIBFDISK_DEBUG_WIPE,    "disklabel items" },
 	{ NULL, 0 }
 };
 
diff --git a/libfdisk/src/item.c b/libfdisk/src/item.c
new file mode 100644
index 000000000..6da00796d
--- /dev/null
+++ b/libfdisk/src/item.c
@@ -0,0 +1,243 @@
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: labelitem
+ * @title: Labelitem
+ * @short_description: disk label items
+ *
+ * The labelitem is label specific items stored in the partition table header.
+ * The information provided by labelitems are not specific to the partitions.
+ *
+ * For example
+ *
+ * <informalexample>
+ *  <programlisting>
+ *	struct fdisk_labelitem *item = fdisk_new_labelitem();
+ *
+ *	fdisk_get_disklabel_item(cxt, GPT_LABELITEM_ALTLBA, item);
+ *	print("Backup header LBA: %ju\n", fdisk_labelitem_get_data_u64(item));
+ *
+ *	fdisk_unref_labelitem(item);
+ *  </programlisting>
+ * </informalexample>
+ *
+ * returns LBA of the alternative GPT header.
+ *
+ * See also fdisk_get_disklabel_item(). The IDs are generic (e.g.
+ * FDISK_LABEL_ITEM_*) and label specific ((e.g. GPT_LABELITEM_*).
+ */
+
+/**
+ * fdisk_new_labelitem
+ *
+ * Returns: new instance.
+ *
+ * Since: v2.29
+ */
+struct fdisk_labelitem *fdisk_new_labelitem(void)
+{
+	struct fdisk_labelitem *li = calloc(1, sizeof(*li));
+
+	li->refcount = 1;
+	DBG(ITEM, ul_debugobj(li, "alloc"));
+	return li;
+}
+
+/**
+ * fdisk_ref_labelitem:
+ * @li: label item
+ *
+ * Incremparts reference counter.
+ *
+ * Since: v2.29
+ */
+void fdisk_ref_labelitem(struct fdisk_labelitem *li)
+{
+	if (li)
+		li->refcount++;
+}
+
+/**
+ * fdisk_reset_labelitem:
+ * @li: label item
+ *
+ * Zeroize data stored in the @li (does not modify anything in disk label).
+ *
+ * Since: v2.29
+ */
+int fdisk_reset_labelitem(struct fdisk_labelitem *li)
+{
+	int refcount;
+
+	if (!li)
+		return -EINVAL;
+	if (li->type == 's')
+		free(li->data.str);
+
+	refcount = li->refcount;
+	memset(li, 0, sizeof(*li));
+	li->refcount = refcount;
+	return 0;
+}
+
+/**
+ * fdisk_unref_labelitem:
+ * @li: label item
+ *
+ * De-incremparts reference counter, on zero the @li is automatically
+ * deallocated.
+ *
+ * Since: v2.29
+ */
+void fdisk_unref_labelitem(struct fdisk_labelitem *li)
+{
+	if (!li)
+		return;
+
+	li->refcount--;
+	if (li->refcount <= 0) {
+		DBG(ITEM, ul_debugobj(li, "free"));
+		fdisk_reset_labelitem(li);
+		free(li);
+	}
+}
+
+/**
+ * fdisk_labelitem_get_name:
+ * @li: label item
+ *
+ * Returns: item name or NULL.
+ * Since: v2.29
+ */
+const char *fdisk_labelitem_get_name(struct fdisk_labelitem *li)
+{
+	return li ? li->name : NULL;
+}
+
+/**
+ * fdisk_labelitem_get_id:
+ * @li: label item
+ *
+ * Returns: item Id or <0 in case of error.
+ * Since: v2.29
+ */
+int fdisk_labelitem_get_id(struct fdisk_labelitem *li)
+{
+	return li ? li->id : -EINVAL;
+}
+
+
+/**
+ * fdisk_labelitem_get_data_u64:
+ * @li: label item
+ * @data: returns data
+ *
+ * Returns: 0 on success, <0 on error
+ * Since: v2.29
+ */
+int fdisk_labelitem_get_data_u64(struct fdisk_labelitem *li, uint64_t *data)
+{
+	if (!li || li->type != 'j')
+		return -EINVAL;
+
+	if (data)
+		*data = li->data.num64;
+	return 0;
+}
+
+/**
+ * fdisk_labelitem_get_data_string:
+ * @li: label item
+ * @data: returns data
+ *
+ * Returns: 0 on success, <0 on error.
+ * Since: v2.29
+ */
+int fdisk_labelitem_get_data_string(struct fdisk_labelitem *li, const char **data)
+{
+	if (!li || li->type != 's')
+		return -EINVAL;
+
+	if (data)
+		*data = li->data.str;
+	return 0;
+}
+
+/**
+ * fdisk_labelitem_is_string:
+ * @li: label item
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_labelitem_is_string(struct fdisk_labelitem *li)
+{
+	return li && li->type == 's';
+}
+
+/**
+ * fdisk_labelitem_is_number:
+ * @li: label item
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_labelitem_is_number(struct fdisk_labelitem *li)
+{
+	return li && li->type == 'j';
+}
+
+#ifdef TEST_PROGRAM
+static int test_listitems(struct fdisk_test *ts, int argc, char *argv[])
+{
+	const char *disk = argv[1];
+	struct fdisk_context *cxt;
+	struct fdisk_labelitem *item;
+	int i = 0, rc;
+
+	cxt = fdisk_new_context();
+	item = fdisk_new_labelitem();
+
+	fdisk_assign_device(cxt, disk, 1);
+
+	do {
+		rc = fdisk_get_disklabel_item(cxt, i++, item);
+		switch (rc) {
+		case 0:	/* success */
+		{
+			const char *name = fdisk_labelitem_get_name(item);
+			const char *str;
+			uint64_t num;
+
+			if (fdisk_labelitem_is_string(item)
+			    && fdisk_labelitem_get_data_string(item, &str) == 0)
+				printf("%s: %s\n", name, str);
+			else if (fdisk_labelitem_get_data_u64(item, &num) == 0)
+				printf("%s: %ju\n", name, num);
+			break;
+		}
+		case 1: /* item unssuported by label -- ignore */
+			rc = 0;
+			break;
+		case 2:	/* end (out of range) */
+			break;
+		default: /* error */
+			break;
+		}
+	} while (rc == 0);
+
+	fdisk_unref_labelitem(item);
+	fdisk_unref_context(cxt);
+	return rc < 0 ? rc : 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+		{ "--list-items",  test_listitems,  "<disk>             list items" },
+		{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libfdisk/src/label.c b/libfdisk/src/label.c
index 3410ed9ba..78fc5e78c 100644
--- a/libfdisk/src/label.c
+++ b/libfdisk/src/label.c
@@ -321,10 +321,9 @@ int fdisk_list_disklabel(struct fdisk_context *cxt)
 		case 's':
 			if (item.data.str && item.name)
 				fdisk_info(cxt, "%s: %s", item.name, item.data.str);
-			free(item.data.str);
-			item.data.str = NULL;
 			break;
 		}
+		fdisk_reset_labelitem(&item);
 	} while (rc == 0 || rc == 1);
 
 	return rc < 0 ? rc : 0;
@@ -426,8 +425,11 @@ int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
 	DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
 
 	rc = fdisk_get_disklabel_item(cxt, FDISK_LABELITEM_ID, &item);
-	if (rc == 0)
+	if (rc == 0) {
 		*id = item.data.str;
+		item.data.str = NULL;
+	}
+	fdisk_reset_labelitem(&item);
 	if (rc > 0)
 		rc = 0;
 	return rc;
@@ -441,7 +443,7 @@ int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
  *
  * Note that @id is always in range 0..N. It's fine to use the function in loop
  * until it returns error or 2, the result in @item should be ignored when
- * function returns 1.
+ * function returns 1. Don't forget to use fdisk_reset_labelitem() or fdisk_unref_labelitem().
  *
  * Returns: 0 on success, < 0 on error, 1 on unssupported item, 2 @id out of range
  */
@@ -450,6 +452,7 @@ int fdisk_get_disklabel_item(struct fdisk_context *cxt, int id, struct fdisk_lab
 	if (!cxt || !cxt->label || !item)
 		return -EINVAL;
 
+	fdisk_reset_labelitem(item);
 	item->id = id;
 	DBG(CXT, ul_debugobj(cxt, "asking for disk %s item %d", cxt->label->name, item->id));
 
diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in
index 2e1a9b5ee..7de305a82 100644
--- a/libfdisk/src/libfdisk.h.in
+++ b/libfdisk/src/libfdisk.h.in
@@ -148,7 +148,6 @@ enum fdisk_asktype {
 	FDISK_ASKTYPE_MENU			/* ask for menu item */
 };
 
-
 /* init.c */
 extern void fdisk_init_debug(int mask);
 
@@ -325,7 +324,20 @@ enum fdisk_labelitem_gen {
 	__FDISK_NLABELITEMS = 8		/* Specifies reserved range for generic items (0..7) */
 };
 
+/* item.c */
+extern struct fdisk_labelitem *fdisk_new_labelitem(void);
+extern void fdisk_ref_labelitem(struct fdisk_labelitem *li);
+extern int fdisk_reset_labelitem(struct fdisk_labelitem *li);
+extern void fdisk_unref_labelitem(struct fdisk_labelitem *li);
+extern const char *fdisk_labelitem_get_name(struct fdisk_labelitem *li);
+extern int fdisk_labelitem_get_id(struct fdisk_labelitem *li);
+extern int fdisk_labelitem_get_data_u64(struct fdisk_labelitem *li, uint64_t *data);
+extern int fdisk_labelitem_get_data_string(struct fdisk_labelitem *li, const char **data);
+extern int fdisk_labelitem_is_string(struct fdisk_labelitem *li);
+extern int fdisk_labelitem_is_number(struct fdisk_labelitem *li);
+
 extern int fdisk_get_disklabel_item(struct fdisk_context *cxt, int id, struct fdisk_labelitem *item);
+
 extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
 extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
 
diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym
index c5aeac68e..49415b3d9 100644
--- a/libfdisk/src/libfdisk.sym
+++ b/libfdisk/src/libfdisk.sym
@@ -262,4 +262,14 @@ FDISK_2.28 {
 
 FDISK_2.29 {
 	fdisk_wipe_partition;
+	fdisk_new_labelitem;
+	fdisk_ref_labelitem;
+	fdisk_reset_labelitem;
+	fdisk_unref_labelitem;
+	fdisk_labelitem_get_name;
+	fdisk_labelitem_get_id;
+	fdisk_labelitem_get_data_u64;
+	fdisk_labelitem_get_data_string;
+	fdisk_labelitem_is_string;
+	fdisk_labelitem_is_number;
 } FDISK_2.28;
-- 
cgit v1.2.3-55-g7522