Skip to content

Commit 71d8d22

Browse files
wedsonaffbq
authored andcommitted
rust: file: add FileDescriptorReservation
Allow for the creation of a file descriptor in two steps: first, we reserve a slot for it, then we commit or drop the reservation. The first step may fail (e.g., the current process ran out of available slots), but commit and drop never fail (and are mutually exclusive). This is needed by Rust Binder when fds are sent from one process to another. It has to be a two-step process to properly handle the case where multiple fds are sent: The operation must fail or succeed atomically, which we achieve by first reserving the fds we need, and only installing the files once we have reserved enough fds to send the files. Fd reservations assume that the value of `current` does not change between the call to get_unused_fd_flags and the call to fd_install (or put_unused_fd). By not implementing the Send trait, this abstraction ensures that the `FileDescriptorReservation` cannot be moved into a different process. Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com> Co-developed-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://lore.kernel.org/r/20231206-alice-file-v2-4-af617c0d9d94@google.com
1 parent 492d054 commit 71d8d22

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

rust/kernel/file.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use crate::{
99
bindings,
1010
cred::Credential,
1111
error::{code::*, Error, Result},
12-
types::{ARef, AlwaysRefCounted, Opaque},
12+
types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque},
1313
};
14-
use core::ptr;
14+
use core::{marker::PhantomData, ptr};
1515

1616
/// Flags associated with a [`File`].
1717
pub mod flags {
@@ -193,6 +193,70 @@ unsafe impl AlwaysRefCounted for File {
193193
}
194194
}
195195

196+
/// A file descriptor reservation.
197+
///
198+
/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
199+
/// then we commit or drop the reservation. The first step may fail (e.g., the current process ran
200+
/// out of available slots), but commit and drop never fail (and are mutually exclusive).
201+
///
202+
/// Dropping the reservation happens in the destructor of this type.
203+
///
204+
/// # Invariants
205+
///
206+
/// The fd stored in this struct must correspond to a reserved file descriptor of the current task.
207+
pub struct FileDescriptorReservation {
208+
fd: u32,
209+
/// Prevent values of this type from being moved to a different task.
210+
///
211+
/// The `fd_install` and `put_unused_fd` functions assume that the value of `current` is
212+
/// unchanged since the call to `get_unused_fd_flags`. By adding this marker to this type, we
213+
/// prevent it from being moved across task boundaries, which ensures that `current` does not
214+
/// change while this value exists.
215+
_not_send: NotThreadSafe,
216+
}
217+
218+
impl FileDescriptorReservation {
219+
/// Creates a new file descriptor reservation.
220+
pub fn get_unused_fd_flags(flags: u32) -> Result<Self> {
221+
// SAFETY: FFI call, there are no safety requirements on `flags`.
222+
let fd: i32 = unsafe { bindings::get_unused_fd_flags(flags) };
223+
if fd < 0 {
224+
return Err(Error::from_errno(fd));
225+
}
226+
Ok(Self {
227+
fd: fd as u32,
228+
_not_send: PhantomData,
229+
})
230+
}
231+
232+
/// Returns the file descriptor number that was reserved.
233+
pub fn reserved_fd(&self) -> u32 {
234+
self.fd
235+
}
236+
237+
/// Commits the reservation.
238+
///
239+
/// The previously reserved file descriptor is bound to `file`. This method consumes the
240+
/// [`FileDescriptorReservation`], so it will not be usable after this call.
241+
pub fn fd_install(self, file: ARef<File>) {
242+
// SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is
243+
// guaranteed to have an owned ref count by its type invariants.
244+
unsafe { bindings::fd_install(self.fd, file.0.get()) };
245+
246+
// `fd_install` consumes both the file descriptor and the file reference, so we cannot run
247+
// the destructors.
248+
core::mem::forget(self);
249+
core::mem::forget(file);
250+
}
251+
}
252+
253+
impl Drop for FileDescriptorReservation {
254+
fn drop(&mut self) {
255+
// SAFETY: `self.fd` was returned by a previous call to `get_unused_fd_flags`.
256+
unsafe { bindings::put_unused_fd(self.fd) };
257+
}
258+
}
259+
196260
/// Represents the `EBADF` error code.
197261
///
198262
/// Used for methods that can only fail with `EBADF`.

rust/kernel/types.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,13 @@ pub enum Either<L, R> {
432432
/// Constructs an instance of [`Either`] containing a value of type `R`.
433433
Right(R),
434434
}
435+
436+
/// Zero-sized type to mark types not [`Send`].
437+
///
438+
/// Add this type as a field to your struct if your type should not be sent to a different task.
439+
/// Since [`Send`] is an auto trait, adding a single field that is `!Send` will ensure that the
440+
/// whole type is `!Send`.
441+
///
442+
/// If a type is `!Send` it is impossible to give control over an instance of the type to another
443+
/// task. This is useful when a type stores task-local information for example file descriptors.
444+
pub type NotThreadSafe = PhantomData<*mut ()>;

0 commit comments

Comments
 (0)