libfdt: Add APIs for iterating and deleting subnodes

In detail, added following APIs for implementing FDT filtering:
  - FdtNodeMut::first_subnode()
  - FdtNodeMut::next_subnode()
  - FdtNodeMut::delete_and_next_subnode()

Bug: 277993056
Test: atest liblibfdt.integration_test
Change-Id: I18f93910ed5d28c4dbb2d38119b6839046775608
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index df57bd3..7eb08b2 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -777,6 +777,40 @@
         fdt_err(ret)
     }
 
+    /// Returns the first subnode of this
+    pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
+
+        Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
+    }
+
+    /// Returns the next subnode that shares the same parent with this
+    pub fn next_subnode(self) -> Result<Option<Self>> {
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
+
+        Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
+    }
+
+    /// Deletes the current node and returns the next subnode
+    pub fn delete_and_next_subnode(mut self) -> Result<Option<Self>> {
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
+
+        let next_offset = fdt_err_or_option(ret)?;
+
+        if Some(self.offset) == next_offset {
+            return Err(FdtError::Internal);
+        }
+
+        // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
+        // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
+        unsafe { self.nop_self()? };
+
+        Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
+    }
+
     fn parent(&'a self) -> Result<FdtNode<'a>> {
         // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
         let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index 0102f9a..e68557f 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -371,3 +371,31 @@
         ]
     );
 }
+
+#[test]
+fn node_mut_delete_and_next_subnode() {
+    let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+    let mut root = fdt.root_mut().unwrap();
+    let mut subnode_iter = root.first_subnode().unwrap();
+
+    while let Some(subnode) = subnode_iter {
+        if subnode.as_node().name() == Ok(cstr!("node_z")) {
+            subnode_iter = subnode.delete_and_next_subnode().unwrap();
+        } else {
+            subnode_iter = subnode.next_subnode().unwrap();
+        }
+    }
+
+    let root = fdt.root().unwrap();
+    let expected_names = vec![
+        Ok(cstr!("node_a")),
+        Ok(cstr!("node_b")),
+        Ok(cstr!("node_c")),
+        Ok(cstr!("__symbols__")),
+    ];
+    let subnode_names: Vec<_> = root.subnodes().unwrap().map(|node| node.name()).collect();
+
+    assert_eq!(expected_names, subnode_names);
+}