| // Copyright 2024 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| use anyhow::Result; | |
| use dbus::arg::PropMap; | |
| use dbus::blocking::Connection; | |
| use dbus_crossroads::Crossroads; | |
| use log::error; | |
| // D-Bus constants. | |
| const MINI_UDISKS_INTERFACE_BLOCK: &str = "org.freedesktop.UDisks2.Block"; | |
| const MINI_UDISKS_INTERFACE_FILESYSTEM: &str = "org.freedesktop.UDisks2.Filesystem"; | |
| const MINI_UDISKS_INTERFACE_MANAGER: &str = "org.freedesktop.UDisks2.Manager"; | |
| const MINI_UDISKS_INTERFACE_PARTITION: &str = "org.freedesktop.UDisks2.Partition"; | |
| const MINI_UDISKS_PATH: &str = "/org/freedesktop/UDisks2"; | |
| const MINI_UDISKS_NAME: &str = "org.freedesktop.UDisks2"; | |
| /// Mount path for the fake ESP. Note that this is the path within | |
| /// fwupd's minijail sandbox; it is a bind mount of | |
| /// `/mnt/stateful_partition/unencrypted/uefi_capsule_updates`. | |
| const ESP_MOUNT_POINT: &str = "/run/uefi_capsule_updates"; | |
| /// Arbitrary name for the ESP disk device. | |
| const FAKE_DISK_NAME: &str = "fakedisk"; | |
| /// Arbitrary ESP partition number. This happens to match the real ESP | |
| /// partition number, but the value doesn't matter. | |
| const ESP_PARTITION_NUM: u32 = 12; | |
| /// Partition type GUID that identifies the ESP. | |
| /// | |
| /// Defined in: | |
| /// https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html | |
| pub const ESP_TYPE_GUID: &'static str = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"; | |
| /// Unique ID for the ESP. | |
| /// | |
| /// This must be a valid GUID, but the value is otherwise arbitrary. | |
| pub const ESP_GUID: &'static str = "99cc6f39-2fd1-4d85-b15a-543e7b023a1f"; | |
| // Create a minimal UDisks2 D-Bus API. This only exposes one block | |
| // device, the a fake ESP partition. Only parts of the UDisks2 API that | |
| // are required by fwupd are exposed. | |
| fn create_dbus_api() -> Result<Crossroads> { | |
| let mut cr = Crossroads::new(); | |
| // D-Bus path for the fake ESP block device: | |
| // "/org/freedesktop/UDisks2/block_devices/fakedisk12". | |
| let dbus_esp_block_device_path = dbus::Path::new(format!( | |
| "{MINI_UDISKS_PATH}/block_devices/{FAKE_DISK_NAME}{ESP_PARTITION_NUM}", | |
| )) | |
| .expect("invalid dbus path"); | |
| // Manager interface. | |
| let dbus_esp_block_device_path_clone = dbus_esp_block_device_path.clone(); | |
| let manager_interface = cr.register(MINI_UDISKS_INTERFACE_MANAGER, move |b| { | |
| b.method( | |
| "GetBlockDevices", | |
| ("options",), | |
| ("block_objects",), | |
| move |_, _, (_,): (PropMap,)| { | |
| let dbus_path = dbus_esp_block_device_path_clone.clone(); | |
| let devices = vec![dbus_path]; | |
| Ok((devices,)) | |
| }, | |
| ); | |
| }); | |
| // Block interface. | |
| let block_interface = cr.register(MINI_UDISKS_INTERFACE_BLOCK, |b| { | |
| b.property("Device") | |
| .get(|_, _| Ok(format!("/dev/{FAKE_DISK_NAME}{ESP_PARTITION_NUM}"))); | |
| }); | |
| // Partition interface. | |
| let partition_interface = cr.register(MINI_UDISKS_INTERFACE_PARTITION, |b| { | |
| b.property("Number").get(|_, _| Ok(ESP_PARTITION_NUM)); | |
| b.property("Type").get(|_, _| Ok(ESP_TYPE_GUID.to_string())); | |
| b.property("UUID").get(|_, _| Ok(ESP_GUID.to_string())); | |
| }); | |
| // Filesystem interface. | |
| let filesystem_interface = cr.register(MINI_UDISKS_INTERFACE_FILESYSTEM, |b| { | |
| b.property("MountPoints") | |
| .get(|_, _| -> Result<Vec<Vec<u8>>, _> { | |
| let mut mount_point = ESP_MOUNT_POINT.as_bytes().to_vec(); | |
| // Add required null terminator. | |
| mount_point.push(0); | |
| Ok(vec![mount_point]) | |
| }); | |
| }); | |
| cr.insert( | |
| format!("{MINI_UDISKS_PATH}/Manager"), | |
| &[manager_interface], | |
| (), | |
| ); | |
| cr.insert( | |
| dbus_esp_block_device_path, | |
| &[block_interface, partition_interface, filesystem_interface], | |
| (), | |
| ); | |
| Ok(cr) | |
| } | |
| fn run_daemon(api: Crossroads) -> Result<()> { | |
| let conn = Connection::new_system()?; | |
| conn.request_name( | |
| MINI_UDISKS_NAME, | |
| /*allow_replacement=*/ false, | |
| /*replace_existing=*/ true, | |
| /*do_not_queue=*/ false, | |
| )?; | |
| api.serve(&conn)?; | |
| unreachable!(); | |
| } | |
| fn main() -> Result<()> { | |
| libchromeos::panic_handler::install_memfd_handler(); | |
| libchromeos::syslog::init("mini-udisks".to_string(), /*log_to_stderr=*/ true) | |
| .expect("failed to initialize logger"); | |
| let api = match create_dbus_api() { | |
| Ok(api) => api, | |
| Err(err) => { | |
| error!("failed to create dbus api: {err}"); | |
| return Err(err); | |
| } | |
| }; | |
| if let Err(err) = run_daemon(api) { | |
| error!("daemon error: {err}"); | |
| Err(err) | |
| } else { | |
| Ok(()) | |
| } | |
| } |