diff mbox series

[1/2] efi_loader: move load options to new module

Message ID 20210115180250.154660-2-xypron.glpk@gmx.de
State Superseded, archived
Delegated to: Heinrich Schuchardt
Headers show
Series efi_loader: make the UEFI boot manager configurable | expand

Commit Message

Heinrich Schuchardt Jan. 15, 2021, 6:02 p.m. UTC
Move all load options related functions to a new module. So that they can
be compiled independently.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
 lib/efi_loader/efi_load_options.c | 151 ++++++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)
 create mode 100644 lib/efi_loader/efi_load_options.c

--
2.29.2

Comments

AKASHI Takahiro Jan. 18, 2021, 2:17 a.m. UTC | #1
On Fri, Jan 15, 2021 at 07:02:49PM +0100, Heinrich Schuchardt wrote:
> Move all load options related functions to a new module. So that they can
> be compiled independently.
> 
> Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> ---
>  lib/efi_loader/efi_load_options.c | 151 ++++++++++++++++++++++++++++++
>  1 file changed, 151 insertions(+)
>  create mode 100644 lib/efi_loader/efi_load_options.c
> 
> diff --git a/lib/efi_loader/efi_load_options.c b/lib/efi_loader/efi_load_options.c
> new file mode 100644
> index 0000000000..9f0e25b6e9
> --- /dev/null
> +++ b/lib/efi_loader/efi_load_options.c
> @@ -0,0 +1,151 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  EFI boot manager
> + *
> + *  Copyright (c) 2017 Rob Clark

Some part of the code in this file was originally written by me.

-Takahiro Akashi

> + */
> +
> +#define LOG_CATEGORY LOGC_EFI
> +
> +#include <common.h>
> +#include <charset.h>
> +#include <log.h>
> +#include <malloc.h>
> +#include <efi_loader.h>
> +//#include <efi_variable.h>
> +#include <asm/unaligned.h>
> +
> +/**
> + * efi_set_load_options() - set the load options of a loaded image
> + *
> + * @handle:		the image handle
> + * @load_options_size:	size of load options
> + * @load_options:	pointer to load options
> + * Return:		status code
> + */
> +efi_status_t efi_set_load_options(efi_handle_t handle,
> +				  efi_uintn_t load_options_size,
> +				  void *load_options)
> +{
> +	struct efi_loaded_image *loaded_image_info;
> +	efi_status_t ret;
> +
> +	ret = EFI_CALL(systab.boottime->open_protocol(
> +					handle,
> +					&efi_guid_loaded_image,
> +					(void **)&loaded_image_info,
> +					efi_root, NULL,
> +					EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL));
> +	if (ret != EFI_SUCCESS)
> +		return EFI_INVALID_PARAMETER;
> +
> +	loaded_image_info->load_options = load_options;
> +	loaded_image_info->load_options_size = load_options_size;
> +
> +	return EFI_CALL(systab.boottime->close_protocol(handle,
> +							&efi_guid_loaded_image,
> +							efi_root, NULL));
> +}
> +
> +/**
> + * efi_deserialize_load_option() - parse serialized data
> + *
> + * Parse serialized data describing a load option and transform it to the
> + * efi_load_option structure.
> + *
> + * @lo:		pointer to target
> + * @data:	serialized data
> + * @size:	size of the load option, on return size of the optional data
> + * Return:	status code
> + */
> +efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
> +					 efi_uintn_t *size)
> +{
> +	efi_uintn_t len;
> +
> +	len = sizeof(u32);
> +	if (*size < len + 2 * sizeof(u16))
> +		return EFI_INVALID_PARAMETER;
> +	lo->attributes = get_unaligned_le32(data);
> +	data += len;
> +	*size -= len;
> +
> +	len = sizeof(u16);
> +	lo->file_path_length = get_unaligned_le16(data);
> +	data += len;
> +	*size -= len;
> +
> +	lo->label = (u16 *)data;
> +	len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
> +	if (lo->label[len])
> +		return EFI_INVALID_PARAMETER;
> +	len = (len + 1) * sizeof(u16);
> +	if (*size < len)
> +		return EFI_INVALID_PARAMETER;
> +	data += len;
> +	*size -= len;
> +
> +	len = lo->file_path_length;
> +	if (*size < len)
> +		return EFI_INVALID_PARAMETER;
> +	lo->file_path = (struct efi_device_path *)data;
> +	if (efi_dp_check_length(lo->file_path, len) < 0)
> +		return EFI_INVALID_PARAMETER;
> +	data += len;
> +	*size -= len;
> +
> +	lo->optional_data = data;
> +
> +	return EFI_SUCCESS;
> +}
> +
> +/**
> + * efi_serialize_load_option() - serialize load option
> + *
> + * Serialize efi_load_option structure into byte stream for BootXXXX.
> + *
> + * @data:	buffer for serialized data
> + * @lo:		load option
> + * Return:	size of allocated buffer
> + */
> +unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
> +{
> +	unsigned long label_len;
> +	unsigned long size;
> +	u8 *p;
> +
> +	label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
> +
> +	/* total size */
> +	size = sizeof(lo->attributes);
> +	size += sizeof(lo->file_path_length);
> +	size += label_len;
> +	size += lo->file_path_length;
> +	if (lo->optional_data)
> +		size += (utf8_utf16_strlen((const char *)lo->optional_data)
> +					   + 1) * sizeof(u16);
> +	p = malloc(size);
> +	if (!p)
> +		return 0;
> +
> +	/* copy data */
> +	*data = p;
> +	memcpy(p, &lo->attributes, sizeof(lo->attributes));
> +	p += sizeof(lo->attributes);
> +
> +	memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
> +	p += sizeof(lo->file_path_length);
> +
> +	memcpy(p, lo->label, label_len);
> +	p += label_len;
> +
> +	memcpy(p, lo->file_path, lo->file_path_length);
> +	p += lo->file_path_length;
> +
> +	if (lo->optional_data) {
> +		utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
> +		p += sizeof(u16); /* size of trailing \0 */
> +	}
> +	return size;
> +}
> +
> --
> 2.29.2
>
Heinrich Schuchardt Jan. 18, 2021, 7:38 a.m. UTC | #2
On 1/18/21 3:17 AM, AKASHI Takahiro wrote:
> On Fri, Jan 15, 2021 at 07:02:49PM +0100, Heinrich Schuchardt wrote:
>> Move all load options related functions to a new module. So that they can
>> be compiled independently.
>>
>> Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
>> ---
>>   lib/efi_loader/efi_load_options.c | 151 ++++++++++++++++++++++++++++++
>>   1 file changed, 151 insertions(+)
>>   create mode 100644 lib/efi_loader/efi_load_options.c
>>
>> diff --git a/lib/efi_loader/efi_load_options.c b/lib/efi_loader/efi_load_options.c
>> new file mode 100644
>> index 0000000000..9f0e25b6e9
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_load_options.c
>> @@ -0,0 +1,151 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + *  EFI boot manager
>> + *
>> + *  Copyright (c) 2017 Rob Clark
>
> Some part of the code in this file was originally written by me.

I will add your name. Other projects simply say don't put names into the
text because their will be further contributors anyways.

Best regards

Heinrich

>
> -Takahiro Akashi
>
>> + */
>> +
>> +#define LOG_CATEGORY LOGC_EFI
>> +
>> +#include <common.h>
>> +#include <charset.h>
>> +#include <log.h>
>> +#include <malloc.h>
>> +#include <efi_loader.h>
>> +//#include <efi_variable.h>
>> +#include <asm/unaligned.h>
>> +
>> +/**
>> + * efi_set_load_options() - set the load options of a loaded image
>> + *
>> + * @handle:		the image handle
>> + * @load_options_size:	size of load options
>> + * @load_options:	pointer to load options
>> + * Return:		status code
>> + */
>> +efi_status_t efi_set_load_options(efi_handle_t handle,
>> +				  efi_uintn_t load_options_size,
>> +				  void *load_options)
>> +{
>> +	struct efi_loaded_image *loaded_image_info;
>> +	efi_status_t ret;
>> +
>> +	ret = EFI_CALL(systab.boottime->open_protocol(
>> +					handle,
>> +					&efi_guid_loaded_image,
>> +					(void **)&loaded_image_info,
>> +					efi_root, NULL,
>> +					EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL));
>> +	if (ret != EFI_SUCCESS)
>> +		return EFI_INVALID_PARAMETER;
>> +
>> +	loaded_image_info->load_options = load_options;
>> +	loaded_image_info->load_options_size = load_options_size;
>> +
>> +	return EFI_CALL(systab.boottime->close_protocol(handle,
>> +							&efi_guid_loaded_image,
>> +							efi_root, NULL));
>> +}
>> +
>> +/**
>> + * efi_deserialize_load_option() - parse serialized data
>> + *
>> + * Parse serialized data describing a load option and transform it to the
>> + * efi_load_option structure.
>> + *
>> + * @lo:		pointer to target
>> + * @data:	serialized data
>> + * @size:	size of the load option, on return size of the optional data
>> + * Return:	status code
>> + */
>> +efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
>> +					 efi_uintn_t *size)
>> +{
>> +	efi_uintn_t len;
>> +
>> +	len = sizeof(u32);
>> +	if (*size < len + 2 * sizeof(u16))
>> +		return EFI_INVALID_PARAMETER;
>> +	lo->attributes = get_unaligned_le32(data);
>> +	data += len;
>> +	*size -= len;
>> +
>> +	len = sizeof(u16);
>> +	lo->file_path_length = get_unaligned_le16(data);
>> +	data += len;
>> +	*size -= len;
>> +
>> +	lo->label = (u16 *)data;
>> +	len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
>> +	if (lo->label[len])
>> +		return EFI_INVALID_PARAMETER;
>> +	len = (len + 1) * sizeof(u16);
>> +	if (*size < len)
>> +		return EFI_INVALID_PARAMETER;
>> +	data += len;
>> +	*size -= len;
>> +
>> +	len = lo->file_path_length;
>> +	if (*size < len)
>> +		return EFI_INVALID_PARAMETER;
>> +	lo->file_path = (struct efi_device_path *)data;
>> +	if (efi_dp_check_length(lo->file_path, len) < 0)
>> +		return EFI_INVALID_PARAMETER;
>> +	data += len;
>> +	*size -= len;
>> +
>> +	lo->optional_data = data;
>> +
>> +	return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> + * efi_serialize_load_option() - serialize load option
>> + *
>> + * Serialize efi_load_option structure into byte stream for BootXXXX.
>> + *
>> + * @data:	buffer for serialized data
>> + * @lo:		load option
>> + * Return:	size of allocated buffer
>> + */
>> +unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
>> +{
>> +	unsigned long label_len;
>> +	unsigned long size;
>> +	u8 *p;
>> +
>> +	label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
>> +
>> +	/* total size */
>> +	size = sizeof(lo->attributes);
>> +	size += sizeof(lo->file_path_length);
>> +	size += label_len;
>> +	size += lo->file_path_length;
>> +	if (lo->optional_data)
>> +		size += (utf8_utf16_strlen((const char *)lo->optional_data)
>> +					   + 1) * sizeof(u16);
>> +	p = malloc(size);
>> +	if (!p)
>> +		return 0;
>> +
>> +	/* copy data */
>> +	*data = p;
>> +	memcpy(p, &lo->attributes, sizeof(lo->attributes));
>> +	p += sizeof(lo->attributes);
>> +
>> +	memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
>> +	p += sizeof(lo->file_path_length);
>> +
>> +	memcpy(p, lo->label, label_len);
>> +	p += label_len;
>> +
>> +	memcpy(p, lo->file_path, lo->file_path_length);
>> +	p += lo->file_path_length;
>> +
>> +	if (lo->optional_data) {
>> +		utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
>> +		p += sizeof(u16); /* size of trailing \0 */
>> +	}
>> +	return size;
>> +}
>> +
>> --
>> 2.29.2
>>
Ilias Apalodimas Jan. 19, 2021, 7:42 a.m. UTC | #3
Hi Heinrich,

Overall the patch and idea seem fine.
Would it make sense to name the file differently?
Something similar to what I did on my initrd  patches, i.e efi_helper.c, so we
can start adding helper functions that have a wider usage?


Thanks
/Ilias

On Fri, Jan 15, 2021 at 07:02:49PM +0100, Heinrich Schuchardt wrote:
> Move all load options related functions to a new module. So that they can
> be compiled independently.
> 
> Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> ---
>  lib/efi_loader/efi_load_options.c | 151 ++++++++++++++++++++++++++++++
>  1 file changed, 151 insertions(+)
>  create mode 100644 lib/efi_loader/efi_load_options.c
> 
> diff --git a/lib/efi_loader/efi_load_options.c b/lib/efi_loader/efi_load_options.c
> new file mode 100644
> index 0000000000..9f0e25b6e9
> --- /dev/null
> +++ b/lib/efi_loader/efi_load_options.c
> @@ -0,0 +1,151 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  EFI boot manager
> + *
> + *  Copyright (c) 2017 Rob Clark
> + */
> +
> +#define LOG_CATEGORY LOGC_EFI
> +
> +#include <common.h>
> +#include <charset.h>
> +#include <log.h>
> +#include <malloc.h>
> +#include <efi_loader.h>
> +//#include <efi_variable.h>
> +#include <asm/unaligned.h>
> +
> +/**
> + * efi_set_load_options() - set the load options of a loaded image
> + *
> + * @handle:		the image handle
> + * @load_options_size:	size of load options
> + * @load_options:	pointer to load options
> + * Return:		status code
> + */
> +efi_status_t efi_set_load_options(efi_handle_t handle,
> +				  efi_uintn_t load_options_size,
> +				  void *load_options)
> +{
> +	struct efi_loaded_image *loaded_image_info;
> +	efi_status_t ret;
> +
> +	ret = EFI_CALL(systab.boottime->open_protocol(
> +					handle,
> +					&efi_guid_loaded_image,
> +					(void **)&loaded_image_info,
> +					efi_root, NULL,
> +					EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL));
> +	if (ret != EFI_SUCCESS)
> +		return EFI_INVALID_PARAMETER;
> +
> +	loaded_image_info->load_options = load_options;
> +	loaded_image_info->load_options_size = load_options_size;
> +
> +	return EFI_CALL(systab.boottime->close_protocol(handle,
> +							&efi_guid_loaded_image,
> +							efi_root, NULL));
> +}
> +
> +/**
> + * efi_deserialize_load_option() - parse serialized data
> + *
> + * Parse serialized data describing a load option and transform it to the
> + * efi_load_option structure.
> + *
> + * @lo:		pointer to target
> + * @data:	serialized data
> + * @size:	size of the load option, on return size of the optional data
> + * Return:	status code
> + */
> +efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
> +					 efi_uintn_t *size)
> +{
> +	efi_uintn_t len;
> +
> +	len = sizeof(u32);
> +	if (*size < len + 2 * sizeof(u16))
> +		return EFI_INVALID_PARAMETER;
> +	lo->attributes = get_unaligned_le32(data);
> +	data += len;
> +	*size -= len;
> +
> +	len = sizeof(u16);
> +	lo->file_path_length = get_unaligned_le16(data);
> +	data += len;
> +	*size -= len;
> +
> +	lo->label = (u16 *)data;
> +	len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
> +	if (lo->label[len])
> +		return EFI_INVALID_PARAMETER;
> +	len = (len + 1) * sizeof(u16);
> +	if (*size < len)
> +		return EFI_INVALID_PARAMETER;
> +	data += len;
> +	*size -= len;
> +
> +	len = lo->file_path_length;
> +	if (*size < len)
> +		return EFI_INVALID_PARAMETER;
> +	lo->file_path = (struct efi_device_path *)data;
> +	if (efi_dp_check_length(lo->file_path, len) < 0)
> +		return EFI_INVALID_PARAMETER;
> +	data += len;
> +	*size -= len;
> +
> +	lo->optional_data = data;
> +
> +	return EFI_SUCCESS;
> +}
> +
> +/**
> + * efi_serialize_load_option() - serialize load option
> + *
> + * Serialize efi_load_option structure into byte stream for BootXXXX.
> + *
> + * @data:	buffer for serialized data
> + * @lo:		load option
> + * Return:	size of allocated buffer
> + */
> +unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
> +{
> +	unsigned long label_len;
> +	unsigned long size;
> +	u8 *p;
> +
> +	label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
> +
> +	/* total size */
> +	size = sizeof(lo->attributes);
> +	size += sizeof(lo->file_path_length);
> +	size += label_len;
> +	size += lo->file_path_length;
> +	if (lo->optional_data)
> +		size += (utf8_utf16_strlen((const char *)lo->optional_data)
> +					   + 1) * sizeof(u16);
> +	p = malloc(size);
> +	if (!p)
> +		return 0;
> +
> +	/* copy data */
> +	*data = p;
> +	memcpy(p, &lo->attributes, sizeof(lo->attributes));
> +	p += sizeof(lo->attributes);
> +
> +	memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
> +	p += sizeof(lo->file_path_length);
> +
> +	memcpy(p, lo->label, label_len);
> +	p += label_len;
> +
> +	memcpy(p, lo->file_path, lo->file_path_length);
> +	p += lo->file_path_length;
> +
> +	if (lo->optional_data) {
> +		utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
> +		p += sizeof(u16); /* size of trailing \0 */
> +	}
> +	return size;
> +}
> +
> --
> 2.29.2
>
Heinrich Schuchardt Jan. 19, 2021, 11:03 a.m. UTC | #4
On 19.01.21 08:42, Ilias Apalodimas wrote:
> Hi Heinrich,
>
> Overall the patch and idea seem fine.
> Would it make sense to name the file differently?
> Something similar to what I did on my initrd  patches, i.e efi_helper.c, so we
> can start adding helper functions that have a wider usage?

efi_helper.c as a name does not convey which functions to find there.

Concerning your initrd patch series:

efi_get_var() would fit into efi_var_common.c.
efi_dp_instance_by_idx() would fit into efi_device_path.c.
create_boot_var_indexed() seems to duplicate efi_create_indexed_name()
which is in efi_string.c.
Leaves us only with efi_get_fp_from_boot() which is only relevant for
the boot manager.

Best regards

Heinrich

>
>
> Thanks
> /Ilias
>
> On Fri, Jan 15, 2021 at 07:02:49PM +0100, Heinrich Schuchardt wrote:
>> Move all load options related functions to a new module. So that they can
>> be compiled independently.
>>
>> Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
>> ---
>>  lib/efi_loader/efi_load_options.c | 151 ++++++++++++++++++++++++++++++
>>  1 file changed, 151 insertions(+)
>>  create mode 100644 lib/efi_loader/efi_load_options.c
diff mbox series

Patch

diff --git a/lib/efi_loader/efi_load_options.c b/lib/efi_loader/efi_load_options.c
new file mode 100644
index 0000000000..9f0e25b6e9
--- /dev/null
+++ b/lib/efi_loader/efi_load_options.c
@@ -0,0 +1,151 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  EFI boot manager
+ *
+ *  Copyright (c) 2017 Rob Clark
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <charset.h>
+#include <log.h>
+#include <malloc.h>
+#include <efi_loader.h>
+//#include <efi_variable.h>
+#include <asm/unaligned.h>
+
+/**
+ * efi_set_load_options() - set the load options of a loaded image
+ *
+ * @handle:		the image handle
+ * @load_options_size:	size of load options
+ * @load_options:	pointer to load options
+ * Return:		status code
+ */
+efi_status_t efi_set_load_options(efi_handle_t handle,
+				  efi_uintn_t load_options_size,
+				  void *load_options)
+{
+	struct efi_loaded_image *loaded_image_info;
+	efi_status_t ret;
+
+	ret = EFI_CALL(systab.boottime->open_protocol(
+					handle,
+					&efi_guid_loaded_image,
+					(void **)&loaded_image_info,
+					efi_root, NULL,
+					EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL));
+	if (ret != EFI_SUCCESS)
+		return EFI_INVALID_PARAMETER;
+
+	loaded_image_info->load_options = load_options;
+	loaded_image_info->load_options_size = load_options_size;
+
+	return EFI_CALL(systab.boottime->close_protocol(handle,
+							&efi_guid_loaded_image,
+							efi_root, NULL));
+}
+
+/**
+ * efi_deserialize_load_option() - parse serialized data
+ *
+ * Parse serialized data describing a load option and transform it to the
+ * efi_load_option structure.
+ *
+ * @lo:		pointer to target
+ * @data:	serialized data
+ * @size:	size of the load option, on return size of the optional data
+ * Return:	status code
+ */
+efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
+					 efi_uintn_t *size)
+{
+	efi_uintn_t len;
+
+	len = sizeof(u32);
+	if (*size < len + 2 * sizeof(u16))
+		return EFI_INVALID_PARAMETER;
+	lo->attributes = get_unaligned_le32(data);
+	data += len;
+	*size -= len;
+
+	len = sizeof(u16);
+	lo->file_path_length = get_unaligned_le16(data);
+	data += len;
+	*size -= len;
+
+	lo->label = (u16 *)data;
+	len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
+	if (lo->label[len])
+		return EFI_INVALID_PARAMETER;
+	len = (len + 1) * sizeof(u16);
+	if (*size < len)
+		return EFI_INVALID_PARAMETER;
+	data += len;
+	*size -= len;
+
+	len = lo->file_path_length;
+	if (*size < len)
+		return EFI_INVALID_PARAMETER;
+	lo->file_path = (struct efi_device_path *)data;
+	if (efi_dp_check_length(lo->file_path, len) < 0)
+		return EFI_INVALID_PARAMETER;
+	data += len;
+	*size -= len;
+
+	lo->optional_data = data;
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_serialize_load_option() - serialize load option
+ *
+ * Serialize efi_load_option structure into byte stream for BootXXXX.
+ *
+ * @data:	buffer for serialized data
+ * @lo:		load option
+ * Return:	size of allocated buffer
+ */
+unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
+{
+	unsigned long label_len;
+	unsigned long size;
+	u8 *p;
+
+	label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
+
+	/* total size */
+	size = sizeof(lo->attributes);
+	size += sizeof(lo->file_path_length);
+	size += label_len;
+	size += lo->file_path_length;
+	if (lo->optional_data)
+		size += (utf8_utf16_strlen((const char *)lo->optional_data)
+					   + 1) * sizeof(u16);
+	p = malloc(size);
+	if (!p)
+		return 0;
+
+	/* copy data */
+	*data = p;
+	memcpy(p, &lo->attributes, sizeof(lo->attributes));
+	p += sizeof(lo->attributes);
+
+	memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
+	p += sizeof(lo->file_path_length);
+
+	memcpy(p, lo->label, label_len);
+	p += label_len;
+
+	memcpy(p, lo->file_path, lo->file_path_length);
+	p += lo->file_path_length;
+
+	if (lo->optional_data) {
+		utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
+		p += sizeof(u16); /* size of trailing \0 */
+	}
+	return size;
+}
+