| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use fuchsia_zircon::{self as zx, AsHandleRef, Vmo}; |
| use std::fs; |
| use std::os::unix::io::AsRawFd; |
| |
| #[allow(bad_style)] |
| #[cfg_attr(test, allow(dead_code))] |
| mod sys; |
| /// The list of sysconfig sub partitions. |
| pub use sys::SysconfigPartition; |
| #[cfg(not(test))] |
| use sys::*; |
| |
| #[cfg(test)] |
| mod sys_mock; |
| #[cfg(test)] |
| use sys_mock::*; |
| |
| pub mod channel; |
| |
| struct SysconfigSyncClient(*mut sysconfig_sync_client_t); |
| |
| impl SysconfigSyncClient { |
| fn new(fd: std::os::unix::io::RawFd) -> Result<Self, zx::Status> { |
| let mut sync_client: *mut sysconfig_sync_client_t = std::ptr::null_mut(); |
| // Caller is responsible for closing the fd. |
| // On success, `sync_client` will be pointing to a `sysconfig_sync_client` struct, and must |
| // be freed with `sysconfig_sync_client_free`. |
| zx::ok(unsafe { |
| sysconfig_sync_client_create(fd, &mut sync_client as *mut *mut sysconfig_sync_client_t) |
| })?; |
| Ok(SysconfigSyncClient(sync_client)) |
| } |
| |
| fn read_partition(&self, partition: SysconfigPartition) -> Result<Vec<u8>, zx::Status> { |
| let size = self.get_partition_size(partition); |
| let vmo = Vmo::create(size as u64)?; |
| assert!(!self.0.is_null()); |
| // Requires that `self.0` is pointing to a valid `sysconfig_sync_client_t`. |
| // Does not take ownership of the vmo handle. |
| zx::ok(unsafe { sysconfig_read_partition(self.0, partition, vmo.raw_handle(), 0) })?; |
| |
| let mut result = vec![0u8; size]; |
| vmo.read(result.as_mut_slice(), 0)?; |
| Ok(result) |
| } |
| |
| fn write_partition( |
| &self, |
| partition: SysconfigPartition, |
| data: &[u8], |
| ) -> Result<(), zx::Status> { |
| let size = self.get_partition_size(partition); |
| if data.len() > size { |
| return Err(zx::Status::OUT_OF_RANGE); |
| } |
| let vmo = Vmo::create(size as u64)?; |
| vmo.write(data, 0)?; |
| assert!(!self.0.is_null()); |
| // Requires that `self.0` is pointing to a valid `sysconfig_sync_client_t`. |
| // Does not take ownership of the vmo handle. |
| zx::ok(unsafe { sysconfig_write_partition(self.0, partition, vmo.raw_handle(), 0) })?; |
| Ok(()) |
| } |
| |
| fn get_partition_size(&self, partition: SysconfigPartition) -> usize { |
| assert!(!self.0.is_null()); |
| // Requires that `self.0` is pointing to a valid `sysconfig_sync_client_t`. |
| unsafe { sysconfig_get_partition_size(self.0, partition) } |
| } |
| } |
| |
| impl Drop for SysconfigSyncClient { |
| fn drop(&mut self) { |
| assert!(!self.0.is_null()); |
| // Requires that `self.0` is pointing to a valid `sysconfig_sync_client_t`. |
| unsafe { |
| sysconfig_sync_client_free(self.0); |
| } |
| } |
| } |
| |
| /// Read an entire sysconfig sub partition into a Vec<u8>. |
| pub fn read_partition(partition: SysconfigPartition) -> Result<Vec<u8>, std::io::Error> { |
| let dev = fs::File::open("/dev")?; |
| let client = SysconfigSyncClient::new(dev.as_raw_fd())?; |
| |
| Ok(client.read_partition(partition)?) |
| } |
| |
| /// Write `data` to a sysconfig sub partition, if the length of `data` is smaller than the |
| /// partition size, the rest of the partition is filled with 0s. |
| pub fn write_partition(partition: SysconfigPartition, data: &[u8]) -> Result<(), std::io::Error> { |
| let dev = fs::OpenOptions::new().read(true).write(true).open("/dev")?; |
| let client = SysconfigSyncClient::new(dev.as_raw_fd())?; |
| |
| client.write_partition(partition, data)?; |
| Ok(()) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_get_partition_size() { |
| let fd = 42; |
| let client = SysconfigSyncClient::new(fd).unwrap(); |
| unsafe { |
| assert_eq!((*client.0).devfs_root, fd); |
| assert_eq!( |
| (*client.0).partition_size, |
| client.get_partition_size(SysconfigPartition::VerifiedBootMetadataA) |
| ); |
| assert_eq!((*client.0).partition, SysconfigPartition::VerifiedBootMetadataA); |
| } |
| } |
| |
| #[test] |
| fn test_read_partition() { |
| let fd = 42; |
| let client = SysconfigSyncClient::new(fd).unwrap(); |
| unsafe { |
| assert_eq!((*client.0).devfs_root, fd); |
| let data = vec![7; 4096]; |
| sys_mock::set_data(data.clone()); |
| assert_eq!( |
| data, |
| client |
| .read_partition(SysconfigPartition::VerifiedBootMetadataB,) |
| .expect("read_partition") |
| ); |
| assert_eq!((*client.0).partition, SysconfigPartition::VerifiedBootMetadataB); |
| assert_eq!((*client.0).vmo_offset, 0); |
| } |
| } |
| |
| #[test] |
| fn test_write_partition() { |
| let fd = 42; |
| let client = SysconfigSyncClient::new(fd).unwrap(); |
| unsafe { |
| assert_eq!((*client.0).devfs_root, fd); |
| let mut data = vec![1, 2, 3, 4]; |
| client.write_partition(SysconfigPartition::Config, &data).expect("write_partition"); |
| assert_eq!((*client.0).partition, SysconfigPartition::Config); |
| assert_eq!((*client.0).vmo_offset, 0); |
| |
| data.resize((*client.0).partition_size as usize, 0); |
| assert_eq!(sys_mock::get_data(), data); |
| } |
| } |
| } |