Jaewan Kim | 9cf731b | 2024-01-19 14:45:45 +0900 | [diff] [blame^] | 1 | // Copyright 2024 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | //! Implements converting file system to FDT blob |
| 16 | |
| 17 | use anyhow::{anyhow, Context, Result}; |
| 18 | use libfdt::Fdt; |
| 19 | use std::ffi::CString; |
| 20 | use std::fs; |
| 21 | use std::os::unix::ffi::OsStrExt; |
| 22 | use std::path::Path; |
| 23 | |
| 24 | /// Trait for Fdt's file system support |
| 25 | pub trait FsFdt<'a> { |
| 26 | /// Creates a Fdt from /proc/device-tree style directory by wrapping a mutable slice |
| 27 | fn from_fs(fs_path: &Path, fdt_buffer: &'a mut [u8]) -> Result<&'a mut Self>; |
| 28 | } |
| 29 | |
| 30 | impl<'a> FsFdt<'a> for Fdt { |
| 31 | fn from_fs(fs_path: &Path, fdt_buffer: &'a mut [u8]) -> Result<&'a mut Fdt> { |
| 32 | let fdt = Fdt::create_empty_tree(fdt_buffer) |
| 33 | .map_err(|e| anyhow!("Failed to create FDT, {e:?}"))?; |
| 34 | |
| 35 | // Recursively traverse fs_path with DFS algorithm. |
| 36 | let mut stack = vec![fs_path.to_path_buf()]; |
| 37 | while let Some(dir_path) = stack.pop() { |
| 38 | let relative_path = dir_path |
| 39 | .strip_prefix(fs_path) |
| 40 | .context("Internal error. Path does not have expected prefix")? |
| 41 | .as_os_str(); |
| 42 | let fdt_path = |
| 43 | CString::from_vec_with_nul([b"/", relative_path.as_bytes(), b"\0"].concat()) |
| 44 | .context("Internal error. Path is not a valid Fdt path")?; |
| 45 | |
| 46 | let mut node = fdt |
| 47 | .node_mut(&fdt_path) |
| 48 | .map_err(|e| anyhow!("Failed to write FDT, {e:?}"))? |
| 49 | .ok_or_else(|| anyhow!("Internal error when writing VM reference DT"))?; |
| 50 | |
| 51 | let mut subnode_names = vec![]; |
| 52 | let entries = |
| 53 | fs::read_dir(&dir_path).with_context(|| format!("Failed to read {dir_path:?}"))?; |
| 54 | for entry in entries { |
| 55 | let entry = |
| 56 | entry.with_context(|| format!("Failed to get an entry in {dir_path:?}"))?; |
| 57 | let entry_type = |
| 58 | entry.file_type().with_context(|| "Unsupported entry type, {entry:?}")?; |
| 59 | let entry_name = entry.file_name(); // binding to keep name below. |
| 60 | if !entry_name.is_ascii() { |
| 61 | return Err(anyhow!("Unsupported entry name for FDT, {entry:?}")); |
| 62 | } |
| 63 | // Safe to unwrap because validated as an ascii string above. |
| 64 | let name = CString::new(entry_name.as_bytes()).unwrap(); |
| 65 | if entry_type.is_dir() { |
| 66 | stack.push(entry.path()); |
| 67 | subnode_names.push(name); |
| 68 | } else if entry_type.is_file() { |
| 69 | let value = fs::read(&entry.path())?; |
| 70 | |
| 71 | node.setprop(&name, &value) |
| 72 | .map_err(|e| anyhow!("Failed to set FDT property, {e:?}"))?; |
| 73 | } else { |
| 74 | return Err(anyhow!( |
| 75 | "Failed to handle {entry:?}. FDT only uses file or directory" |
| 76 | )); |
| 77 | } |
| 78 | } |
| 79 | // Note: sort() is necessary to prevent FdtError::Exists from add_subnodes(). |
| 80 | // FDT library may omit address in node name when comparing their name, so sort to add |
| 81 | // node without address first. |
| 82 | subnode_names.sort(); |
| 83 | let subnode_names_c_str: Vec<_> = subnode_names.iter().map(|x| x.as_c_str()).collect(); |
| 84 | node.add_subnodes(&subnode_names_c_str) |
| 85 | .map_err(|e| anyhow!("Failed to add node, {e:?}"))?; |
| 86 | } |
| 87 | |
| 88 | Ok(fdt) |
| 89 | } |
| 90 | } |