blob: f953291b13f7a545f4f1b79f25549aed7feb3ffb [file] [log] [blame]
// 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);
}
}
}