blob: 02268e95ad25a4fb7654cf0a67e75f56ed3d9e8e [file] [log] [blame] [edit]
// 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(())
}
}