oem: meta: Add decode_oem_meta_file_io_write_req()

Add decode_oem_meta_file_io_write_req function.
Deprecate decode_oem_meta_file_io_req function.

The difference between functions decode_oem_meta_file_io_req and
decode_oem_meta_file_io_write_req:

- decode_oem_meta_file_io_write_req:
  - return 0 on success and return a negative errno value on failure.
  - input parameters is structure.
  - add req_length parameter to check whether the total length of
    the request is buffer overflow.
- decode_oem_meta_file_io_req:
  - return PLDM_SUCCESS on success and return another PLDM completion
    code on failure.
  - input parameters is passing individual pointers.

Change-Id: Iae3c7f24128bc25c5af6951f34fdc6d8a7b90381
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
Signed-off-by: Lora Lin <lora.lin.wiwynn@gmail.com>
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a4f18d..ae80421 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,23 @@
 
 ## [Unreleased]
 
+### Added
+
+1. oem: meta: Add decode_oem_meta_file_io_write_req()
+
+### Deprecated
+
+1. oem: meta: Deprecate `decode_oem_meta_file_io_req()`
+
+   Users should switch to `decode_oem_meta_file_io_write_req()`. Modify this
+   function to make it safer.
+
+   Modification:
+
+   - The meaning of the returned result.
+   - Change parameters from individual pointers to a struct.
+   - Check the length provided in the message won't exceed the buffer.
+
 ### Removed
 
 1. Deprecated functions with the `_check` suffix
diff --git a/include/libpldm/oem/meta/file_io.h b/include/libpldm/oem/meta/file_io.h
index 32a7109..e30ab47 100644
--- a/include/libpldm/oem/meta/file_io.h
+++ b/include/libpldm/oem/meta/file_io.h
@@ -2,6 +2,8 @@
 #ifndef LIBPLDM_OEM_META_FILE_IO_H
 #define LIBPLDM_OEM_META_FILE_IO_H
 
+#include <libpldm/compiler.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -13,18 +15,42 @@
 /** @brief PLDM Commands in OEM META type
  */
 
-enum pldm_oem_meta_fileio_commands {
-	PLDM_OEM_META_FILEIO_CMD_WRITE_FILE = 0x2,
-	PLDM_OEM_META_FILEIO_CMD_READ_FILE = 0x3,
+enum pldm_oem_meta_file_io_commands {
+	PLDM_OEM_META_FILE_IO_CMD_WRITE_FILE = 0x2,
+	PLDM_OEM_META_FILE_IO_CMD_READ_FILE = 0x3,
 };
 
-struct pldm_oem_meta_write_file_req {
-	uint8_t file_handle;
+struct pldm_oem_meta_file_io_write_req {
+	uint8_t handle;
 	uint32_t length;
-	uint8_t file_data[1];
+#ifndef __cplusplus
+	uint8_t data[] LIBPLDM_CC_COUNTED_BY(length);
+#endif
 };
+#define PLDM_OEM_META_FILE_IO_WRITE_REQ_MIN_LENGTH 5u
 
-/** @brief Decode OEM meta file io req
+/** @brief Obtain the pointer to the data array of a write request
+ *
+ * @param[in] req - The pointer to the write request struct
+ *
+ * @return The write request data pointer.
+ */
+void *pldm_oem_meta_file_io_write_req_data(
+	struct pldm_oem_meta_file_io_write_req *req);
+
+/** @brief Decode OEM meta write file io req
+ *
+ *  @param[in] msg - Pointer to PLDM request message
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] req - Pointer to the structure to store the decoded response data
+ *  @param[in] req_length - Length of request structure
+ *  @return 0 on success, negative errno value on failure
+ */
+int decode_oem_meta_file_io_write_req(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_oem_meta_file_io_write_req *req, size_t req_length);
+
+/** @brief Deprecated decoder for OEM meta write file io req
  *
  *  @param[in] msg - Pointer to PLDM request message
  *  @param[in] payload_length - Length of request payload
diff --git a/src/api.h b/src/api.h
index 3f1fed0..f99f3ed 100644
--- a/src/api.h
+++ b/src/api.h
@@ -32,6 +32,10 @@
 	case -ENOMSG:
 		rc = PLDM_ERROR_INVALID_PLDM_TYPE;
 		break;
+	case -EBADMSG:
+	case -EOVERFLOW:
+		rc = PLDM_ERROR_INVALID_LENGTH;
+		break;
 	default:
 		assert(false);
 		rc = PLDM_ERROR;
diff --git a/src/oem/meta/file_io.c b/src/oem/meta/file_io.c
index 9df6263..ce7d9e3 100644
--- a/src/oem/meta/file_io.c
+++ b/src/oem/meta/file_io.c
@@ -1,41 +1,91 @@
 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
 #include <libpldm/oem/meta/file_io.h>
 #include <endian.h>
+#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
-#include "msgbuf.h"
 
-#define PLDM_OEM_META_DECODE_WRITE_FILE_IO_MIN_SIZE 6
-LIBPLDM_ABI_STABLE
-int decode_oem_meta_file_io_req(const struct pldm_msg *msg,
-				size_t payload_length, uint8_t *file_handle,
-				uint32_t *length, uint8_t *data)
+#include "api.h"
+#include "msgbuf.h"
+#include "dsp/base.h"
+
+LIBPLDM_ABI_TESTING
+void *pldm_oem_meta_file_io_write_req_data(
+	struct pldm_oem_meta_file_io_write_req *req)
+{
+	return req->data;
+}
+
+LIBPLDM_ABI_TESTING
+int decode_oem_meta_file_io_write_req(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_oem_meta_file_io_write_req *req, size_t req_length)
 {
 	struct pldm_msgbuf _buf;
 	struct pldm_msgbuf *buf = &_buf;
 	int rc;
 
-	if (msg == NULL || file_handle == NULL || length == NULL ||
-	    data == NULL) {
-		return PLDM_ERROR_INVALID_DATA;
+	if (msg == NULL || req == NULL) {
+		return -EINVAL;
 	}
 
-	rc = pldm_msgbuf_init_cc(buf,
-				 PLDM_OEM_META_DECODE_WRITE_FILE_IO_MIN_SIZE,
-				 msg->payload, payload_length);
+	if (req_length < sizeof(*req)) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf,
+				    PLDM_OEM_META_FILE_IO_WRITE_REQ_MIN_LENGTH,
+				    msg->payload, payload_length);
 	if (rc) {
 		return rc;
 	}
 
-	pldm_msgbuf_extract_p(buf, file_handle);
-	pldm_msgbuf_extract_p(buf, length);
+	pldm_msgbuf_extract(buf, req->handle);
+	rc = pldm_msgbuf_extract(buf, req->length);
+	if (rc) {
+		return rc;
+	}
 
-	/* NOTE: Memory safety failure */
-	rc = pldm_msgbuf_extract_array_uint8(buf, (size_t)(*length), data,
-					     UINT32_MAX);
+	rc = pldm_msgbuf_extract_array(buf, req->length, req->data,
+				       req_length - sizeof(*req));
 	if (rc) {
 		return rc;
 	}
 
 	return pldm_msgbuf_destroy_consumed(buf);
 }
+
+LIBPLDM_ABI_DEPRECATED
+int decode_oem_meta_file_io_req(const struct pldm_msg *msg,
+				size_t payload_length, uint8_t *file_handle,
+				uint32_t *length, uint8_t *data)
+{
+	struct pldm_oem_meta_file_io_write_req *request_msg;
+	size_t request_msg_len;
+	int rc;
+
+	if (msg == NULL || file_handle == NULL || length == NULL ||
+	    data == NULL) {
+		return pldm_xlate_errno(-EINVAL);
+	}
+
+	request_msg_len = sizeof(*request_msg) + payload_length;
+	request_msg = malloc(request_msg_len);
+
+	rc = decode_oem_meta_file_io_write_req(msg, payload_length, request_msg,
+					       request_msg_len);
+	if (rc < 0) {
+		free(request_msg);
+		return pldm_xlate_errno(rc);
+	}
+
+	*file_handle = request_msg->handle;
+	*length = request_msg->length;
+
+	/* NOTE: Unsafe, memory safety is not possible due to API constraints. */
+	memcpy(data, request_msg->data, request_msg->length);
+
+	free(request_msg);
+
+	return 0;
+}