pdr: Add pldm_pdr_delete_by_record_handle() API

Adds a new libpldm API to delete the PDR record from the PDR repo
based on the record handle of the PDR record.

Change-Id: I44f5568aa024660f7d370d9a2c6b3f9286d96ed8
Signed-off-by: Pavithra Barithaya <pavithrabarithaya07@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d26c123..2230364 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,8 @@
 
 - base: Define the minimum request bytes for SetTID command.
 
+- pdr: Add pldm_pdr_delete_by_record_handle() API
+
 ### Changed
 
 - dsp: firmware_update: Expand "params" in symbol names
diff --git a/include/libpldm/pdr.h b/include/libpldm/pdr.h
index 7db280e..af14ace 100644
--- a/include/libpldm/pdr.h
+++ b/include/libpldm/pdr.h
@@ -234,6 +234,18 @@
 	uint8_t child_index, uint32_t range_exclude_start_handle,
 	uint32_t range_exclude_end_handle, uint16_t *container_id);
 
+/** @brief Delete record using its record handle
+ *
+ *  @param[in] repo - opaque pointer acting as a PDR repo handle
+ *  @param[in] record_handle - record handle of input PDR record
+ *  @param[in] is_remote - if true, then the PDR is not from this terminus
+ *
+ *  @return 0 if deleted successful else returns -EINVAL when repo is NULL
+ *  or -ENOENT if the record handle is not found in the repo.
+ */
+int pldm_pdr_delete_by_record_handle(pldm_pdr *repo, uint32_t record_handle,
+				     bool is_remote);
+
 /* ======================= */
 /* FRU Record Set PDR APIs */
 /* ======================= */
diff --git a/src/dsp/pdr.c b/src/dsp/pdr.c
index 95526ff..efc94d2 100644
--- a/src/dsp/pdr.c
+++ b/src/dsp/pdr.c
@@ -456,6 +456,46 @@
 	return -ENOENT;
 }
 
+LIBPLDM_ABI_TESTING
+int pldm_pdr_delete_by_record_handle(pldm_pdr *repo, uint32_t record_handle,
+				     bool is_remote)
+{
+	pldm_pdr_record *record;
+	pldm_pdr_record *prev = NULL;
+	int rc = 0;
+	uint16_t rec_handle = 0;
+
+	if (!repo) {
+		return -EINVAL;
+	}
+	record = repo->first;
+
+	while (record != NULL) {
+		struct pldm_msgbuf _buf;
+		struct pldm_msgbuf *buf = &_buf;
+		rc = pldm_msgbuf_init_errno(buf, sizeof(struct pldm_pdr_hdr),
+					    record->data, record->size);
+
+		if (rc) {
+			return rc;
+		}
+		if ((rc = pldm_msgbuf_extract(buf, rec_handle))) {
+			return rc;
+		}
+		if (record->is_remote == is_remote &&
+		    rec_handle == record_handle) {
+			prev = pldm_pdr_get_prev_record(repo, record);
+			return pldm_pdr_remove_record(repo, record, prev);
+		}
+		rc = pldm_msgbuf_destroy(buf);
+		if (rc) {
+			return rc;
+		}
+		record = record->next;
+	}
+	return -ENOENT;
+}
+
 typedef struct pldm_entity_association_tree {
 	pldm_entity_node *root;
 	uint16_t last_used_container_id;
diff --git a/tests/dsp/pdr.cpp b/tests/dsp/pdr.cpp
index e8e2256..3b72cb7 100644
--- a/tests/dsp/pdr.cpp
+++ b/tests/dsp/pdr.cpp
@@ -772,6 +772,38 @@
 }
 #endif
 
+#ifdef LIBPLDM_API_TESTING
+TEST(PDRAccess, testRemoveByRecordHandle)
+{
+    std::array<uint8_t, sizeof(pldm_pdr_hdr)> data{};
+
+    auto repo = pldm_pdr_init();
+    uint32_t first = 0;
+    EXPECT_EQ(pldm_pdr_add(repo, data.data(), data.size(), false, 1, &first),
+              0);
+
+    uint32_t second = 0;
+    EXPECT_EQ(pldm_pdr_add(repo, data.data(), data.size(), false, 1, &second),
+              0);
+
+    uint32_t third = 0;
+    EXPECT_EQ(pldm_pdr_add(repo, data.data(), data.size(), false, 1, &third),
+              0);
+
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 3u);
+
+    int rc = pldm_pdr_delete_by_record_handle(repo, 1, false);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 2u);
+
+    rc = pldm_pdr_delete_by_record_handle(repo, 2, false);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 1u);
+
+    pldm_pdr_destroy(repo);
+}
+#endif
+
 TEST(EntityAssociationPDR, testInit)
 {
     auto tree = pldm_entity_association_tree_init();