msgbuf: Add pldm_msgbuf_copy_string_ascii()

Safely copy a NUL-terminated string between msgbuf instances.

Change-Id: I224dc3f5bbd55fd9d4727ab0de065d5253ee0bea
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/src/msgbuf.h b/src/msgbuf.h
index ef65be2..9c0d2ca 100644
--- a/src/msgbuf.h
+++ b/src/msgbuf.h
@@ -1190,6 +1190,21 @@
 	return 0;
 }
 
+__attribute__((always_inline)) static inline int
+pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
+{
+	void *ascii = NULL;
+	size_t len = 0;
+	int rc;
+
+	rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len);
+	if (rc < 0) {
+		return rc;
+	}
+
+	return pldm__msgbuf_insert_array_void(dst, ascii, len);
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tests/msgbuf.cpp b/tests/msgbuf.cpp
index 714243f..b19ee8e 100644
--- a/tests/msgbuf.cpp
+++ b/tests/msgbuf.cpp
@@ -1115,9 +1115,10 @@
               PLDM_SUCCESS);
     EXPECT_EQ(pldm_msgbuf_extract_uint16(dst, &checkVal), PLDM_SUCCESS);
 
-    EXPECT_EQ(buf[0], checkVal);
     EXPECT_EQ(pldm_msgbuf_destroy(src), PLDM_SUCCESS);
     EXPECT_EQ(pldm_msgbuf_destroy(dst), PLDM_SUCCESS);
+
+    EXPECT_EQ(buf[0], checkVal);
 }
 
 TEST(msgbuf, pldm_msgbuf_copy_bad)
@@ -1140,3 +1141,77 @@
     EXPECT_EQ(pldm_msgbuf_copy(dst, src, value, name),
               PLDM_ERROR_INVALID_LENGTH);
 }
+
+TEST(msgbuf, pldm_msgbuf_copy_string_ascii_exact)
+{
+    const char msg[] = "this is a message";
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg)] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_ascii(dst, src), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), 0);
+    EXPECT_EQ(0, memcmp(msg, buf, sizeof(buf)));
+}
+
+TEST(msgbuf, pldm_msgbuf_copy_string_ascii_dst_exceeds_src)
+{
+    const char msg[] = "this is a message";
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg) + 1] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_ascii(dst, src), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), 0);
+    EXPECT_EQ(0, memcmp(buf, msg, sizeof(msg)));
+}
+
+TEST(msgbuf, pldm_msgbuf_copy_string_ascii_src_exceeds_dst)
+{
+    const char msg[] = "this is a message";
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg) - 1] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_ascii(dst, src), -EOVERFLOW);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), -EOVERFLOW);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), 0);
+}
+
+TEST(msgbuf, pldm_msgbuf_copy_string_ascii_unterminated_src)
+{
+    const char msg[] = {'a'};
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg)] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_ascii(dst, src), -EOVERFLOW);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), -EOVERFLOW);
+}