diff --git a/drivers/android/transaction.rs b/drivers/android/transaction.rs index 9c7eb94291700d..94de80dc6f0383 100644 --- a/drivers/android/transaction.rs +++ b/drivers/android/transaction.rs @@ -4,7 +4,7 @@ use alloc::sync::Arc; use core::sync::atomic::{AtomicBool, Ordering}; use kernel::{ io_buffer::IoBufferWriter, linked_list::Links, prelude::*, sync::Ref, - user_ptr::UserSlicePtrWriter, + user_ptr::UserSlicePtrWriter, ScopeGuard, }; use crate::{ @@ -144,6 +144,10 @@ impl DeliverToRead for Transaction { pub sender_pid: pid_t, pub sender_euid: uid_t, */ + let send_failed_reply = ScopeGuard::new(|| { + let reply = Either::Right(BR_FAILED_REPLY); + self.from.deliver_reply(reply, &self); + }); let mut tr = BinderTransactionData::default(); if let Some(nref) = &self.node_ref { @@ -171,13 +175,13 @@ impl DeliverToRead for Transaction { BR_TRANSACTION }; - // Write the transaction code and data to the user buffer. On failure we complete the - // transaction with an error. - if let Err(err) = writer.write(&code).and_then(|_| writer.write(&tr)) { - let reply = Either::Right(BR_FAILED_REPLY); - self.from.deliver_reply(reply, &self); - return Err(err); - } + // Write the transaction code and data to the user buffer. + writer.write(&code)?; + writer.write(&tr)?; + + // Dismiss the completion of transaction with a failure. No failure paths are allowed from + // here on out. + send_failed_reply.dismiss(); // When this is not a reply and not an async transaction, update `current_transaction`. If // it's a reply, `current_transaction` has already been updated appropriately. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 79a4779f4612b0..2c3faf48cc8d33 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -77,7 +77,7 @@ pub mod user_ptr; pub use build_error::build_error; pub use crate::error::{Error, Result}; -pub use crate::types::Mode; +pub use crate::types::{Mode, ScopeGuard}; /// Page size defined in terms of the `PAGE_SHIFT` macro from C. /// diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 63d8477acb9183..12ce3ef6aac651 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -91,3 +91,66 @@ impl PointerWrapper for Pin { Pin::new_unchecked(T::from_pointer(p)) } } + +/// Runs a cleanup function/closure when dropped. +/// +/// The [`ScopeGuard::dismiss`] function prevents the cleanup function from running. +/// +/// # Examples +/// +/// In the example below, we have multiple exit paths and we want to log regardless of which one is +/// taken: +/// ``` +/// fn example1(arg: bool) { +/// let _log = ScopeGuard::new(|| pr_info!("example1 completed\n")); +/// +/// if arg { +/// return; +/// } +/// +/// // Do something... +/// } +/// ``` +/// +/// In the example below, we want to log the same message on all early exits but a different one on +/// the main exit path: +/// ``` +/// fn example2(arg: bool) { +/// let log = ScopeGuard::new(|| pr_info!("example2 returned early\n")); +/// +/// if arg { +/// return; +/// } +/// +/// // (Other early returns...) +/// +/// log.dismiss(); +/// pr_info!("example2 no early return\n"); +/// } +/// ``` +pub struct ScopeGuard { + cleanup_func: Option, +} + +impl ScopeGuard { + /// Creates a new cleanup object with the given cleanup function. + pub fn new(cleanup_func: T) -> Self { + Self { + cleanup_func: Some(cleanup_func), + } + } + + /// Prevents the cleanup function from running. + pub fn dismiss(mut self) { + self.cleanup_func.take(); + } +} + +impl Drop for ScopeGuard { + fn drop(&mut self) { + // Run the cleanup function if one is still present. + if let Some(cleanup) = self.cleanup_func.take() { + cleanup(); + } + } +}