fru: Add an API to delete the FRU record from the FRUTable

This method can be used to remove the FRU record entry from
the FRU record set Table when a FRU is removed during the
Concurrent Maintenance Operation of the FRU. The Concurrent
Maintenance Operation is the ability to replace, remove, or
update a FRU while the system remains operational, without
requiring a full system shutdown or reboot.

Tested:
Removed a Fan FRU from the system - the FRU record is deleted
and verified that the table is updated.

pldmd[940]: Removing Individual FRU [ /xyz/openbmc_project/inventory
/system/chassis/motherboard/fan0 ] with entityid [ 93, 1, 3 ]
pldmd[940]: record is deleted: numRecs before deletion is 207
pldmd[940]: record is deleted: numRecs after deletion is 206
pldmd[940]: Table updated

Change-Id: I3ae9f87fe1afe81c3a3925c24583a622791c781a
Signed-off-by: Pavithra Barithaya <pavithrabarithaya07@gmail.com>
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index e3b89d0..ff6643a 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -343,6 +343,70 @@
     }
 }
 
+void FruImpl::deleteFRURecord(uint16_t rsi)
+{
+    std::vector<uint8_t> updatedFruTbl;
+    size_t pos = 0;
+
+    while (pos < table.size())
+    {
+        // Ensure enough space for the record header
+        if ((table.size() - pos) < sizeof(struct pldm_fru_record_data_format))
+        {
+            // Log or handle corrupt/truncated record
+            error("Error: Incomplete FRU record header");
+            return;
+        }
+
+        auto recordSetSrc =
+            reinterpret_cast<const struct pldm_fru_record_data_format*>(
+                &table[pos]);
+
+        size_t recordLen = sizeof(struct pldm_fru_record_data_format) -
+                           sizeof(struct pldm_fru_record_tlv);
+
+        const struct pldm_fru_record_tlv* tlv = recordSetSrc->tlvs;
+
+        for (uint8_t i = 0; i < recordSetSrc->num_fru_fields; ++i)
+        {
+            if ((table.size() - pos) < (recordLen + sizeof(*tlv)))
+            {
+                error("Error: Incomplete TLV header");
+                return;
+            }
+
+            size_t len = sizeof(*tlv) - 1 + tlv->length;
+
+            if ((table.size() - pos) < (recordLen + len))
+            {
+                error("Error: Incomplete TLV data");
+                return;
+            }
+
+            recordLen += len;
+
+            // Advance to next tlv
+            tlv = reinterpret_cast<const struct pldm_fru_record_tlv*>(
+                reinterpret_cast<const uint8_t*>(tlv) + len);
+        }
+
+        if ((le16toh(recordSetSrc->record_set_id) != rsi && rsi != 0))
+        {
+            std::copy(table.begin() + pos, table.begin() + pos + recordLen,
+                      std::back_inserter(updatedFruTbl));
+        }
+        else
+        {
+            // Deleted record
+            numRecs--;
+        }
+
+        pos += recordLen;
+    }
+    // Replace the old table with the updated one
+    table = std::move(updatedFruTbl);
+}
+
 std::vector<uint8_t> FruImpl::tableResize()
 {
     std::vector<uint8_t> tempTable;
diff --git a/libpldmresponder/fru.hpp b/libpldmresponder/fru.hpp
index d4e0ff4..6586fad 100644
--- a/libpldmresponder/fru.hpp
+++ b/libpldmresponder/fru.hpp
@@ -249,6 +249,13 @@
                          const fru_parser::FruRecordInfos& recordInfos,
                          const pldm_entity& entity);
 
+    /** @brief Deletes a FRU record from record set table.
+     *  @param[in] rsi - the FRU Record Set Identifier
+     *
+     *  @return
+     */
+    void deleteFRURecord(uint16_t rsi);
+
     /** @brief Associate sensor/effecter to FRU entity
      */
     dbus::AssociatedEntityMap associatedEntityMap;