[PATCH v2 089/113] efi: devicepath: implement device_path_to_str_buf variant

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Mar 4 11:00:14 PST 2024


Existing device_path_to_str() formats a buffer once to determines the amount
of space necessary and then allocates it and formats it again.

To make this functionality available as printf format specifier, we need
a variant that doesn't allocate memory and that's preferably single
pass, which we add here.

No functional change for users of device_path_to_str().

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 efi/devicepath.c | 71 +++++++++++++++++++++++++++++++++++-------------
 include/efi.h    |  1 +
 2 files changed, 53 insertions(+), 19 deletions(-)

diff --git a/efi/devicepath.c b/efi/devicepath.c
index d562bf482955..0c2fc4d86927 100644
--- a/efi/devicepath.c
+++ b/efi/devicepath.c
@@ -6,11 +6,13 @@
 #include <malloc.h>
 #include <string.h>
 #include <wchar.h>
+#include <linux/overflow.h>
 #include <efi/device-path.h>
 
 struct string {
 	char *str;
-	int len;
+	unsigned allocated;
+	unsigned used;
 };
 
 char *cprintf(struct string *str, const char *fmt, ...)
@@ -18,17 +20,22 @@ char *cprintf(struct string *str, const char *fmt, ...)
 
 char *cprintf(struct string *str, const char *fmt, ...)
 {
+	void *buf = str->str;
+	unsigned bufsize = 0;
 	va_list args;
 	int len;
 
+	if (str->str) {
+		buf += str->used;
+		if (check_sub_overflow(str->allocated, str->used, &bufsize))
+			bufsize = 0;
+	}
+
 	va_start(args, fmt);
-	if (str->str)
-		len = vsprintf(str->str + str->len, fmt, args);
-	else
-		len = vsnprintf(NULL, 0, fmt, args);
+	len = vsnprintf(buf, bufsize, fmt, args);
 	va_end(args);
 
-	str->len += len;
+	str->used += len;
 
 	return NULL;
 }
@@ -717,8 +724,8 @@ struct {
 	0, 0, NULL}
 };
 
-static int __device_path_to_str(struct string *str,
-				const struct efi_device_path *dev_path)
+static void __device_path_to_str(struct string *str,
+				 const struct efi_device_path *dev_path)
 {
 	const struct efi_device_path *dev_path_node;
 	void (*dump_node) (struct string *, const void *);
@@ -743,32 +750,58 @@ static int __device_path_to_str(struct string *str,
 		if (!dump_node)
 			dump_node = dev_path_node_unknown;
 
-		if (str->len && dump_node != dev_path_end_instance)
+		if (str->used && dump_node != dev_path_end_instance)
 			cprintf(str, "/");
 
 		dump_node(str, dev_path_node);
 
 		dev_path_node = next_device_path_node(dev_path_node);
 	}
-
-	return 0;
 }
 
+/**
+ * device_path_to_str_buf() - formats a device path into a preallocated buffer
+ *
+ * @dev_path:	The EFI device path to format
+ * @buf:	The buffer to format into or optionally NULL if @len is zero
+ * @len:	The number of bytes that may be written into @buf
+ * Return:	total number of bytes that are required to store the formatted
+ *		result, excluding the terminating NUL byte, which is always
+ *		written.
+ */
+size_t device_path_to_str_buf(const struct efi_device_path *dev_path,
+			      char *buf, size_t len)
+{
+	struct string str = {
+		.str = buf,
+		.allocated = len,
+	};
+
+	__device_path_to_str(&str, dev_path);
+
+	return str.used;
+}
+
+/**
+ * device_path_to_str() - formats a device path into a newly allocated buffer
+ *
+ * @dev_path:	The EFI device path to format
+ * Return:	A pointer to the nul-terminated formatted device path.
+ */
 char *device_path_to_str(const struct efi_device_path *dev_path)
 {
-	struct string str = {};
+	void *buf;
+	size_t size;
 
-	__device_path_to_str(&str, dev_path);
+	size = device_path_to_str_buf(dev_path, NULL, 0);
 
-	str.str = malloc(str.len + 1);
-	if (!str.str)
+	buf = malloc(size + 1);
+	if (!buf)
 		return NULL;
 
-	str.len = 0;
+	device_path_to_str_buf(dev_path, buf, size + 1);
 
-	__device_path_to_str(&str, dev_path);
-
-	return str.str;
+	return buf;
 }
 
 u8 device_path_to_type(const struct efi_device_path *dev_path)
diff --git a/include/efi.h b/include/efi.h
index 6795dc414c3c..45e4080fac08 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -880,6 +880,7 @@ struct efi_simple_text_input_protocol {
 
 const struct efi_device_path *device_path_from_handle(efi_handle_t handle);
 char *device_path_to_str(const struct efi_device_path *dev_path);
+size_t device_path_to_str_buf(const struct efi_device_path *dev_path, char buf[], size_t size);
 u8 device_path_to_type(const struct efi_device_path *dev_path);
 u8 device_path_to_subtype(const struct efi_device_path *dev_path);
 char *device_path_to_partuuid(const struct efi_device_path *dev_path);
-- 
2.39.2




More information about the barebox mailing list