Skip to content

Commit 39db1d9

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 <[email protected]> Co-developed-by: Alice Ryhl <[email protected]> Reviewed-by: Benno Lossin <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent f8d1723 commit 39db1d9

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

rust/kernel/file.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
bindings,
1010
cred::Credential,
1111
error::{code::*, Error, Result},
12-
types::{ARef, AlwaysRefCounted, Opaque},
12+
types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque},
1313
};
1414
use core::ptr;
1515

@@ -243,6 +243,76 @@ unsafe impl AlwaysRefCounted for File {
243243
}
244244
}
245245

246+
/// A file descriptor reservation.
247+
///
248+
/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
249+
/// then we commit or drop the reservation. The first step may fail (e.g., the current process ran
250+
/// out of available slots), but commit and drop never fail (and are mutually exclusive).
251+
///
252+
/// Dropping the reservation happens in the destructor of this type.
253+
///
254+
/// # Invariants
255+
///
256+
/// The fd stored in this struct must correspond to a reserved file descriptor of the current task.
257+
pub struct FileDescriptorReservation {
258+
fd: u32,
259+
/// Prevent values of this type from being moved to a different task.
260+
///
261+
/// The `fd_install` and `put_unused_fd` functions assume that the value of `current` is
262+
/// unchanged since the call to `get_unused_fd_flags`. By adding this marker to this type, we
263+
/// prevent it from being moved across task boundaries, which ensures that `current` does not
264+
/// change while this value exists.
265+
_not_send: NotThreadSafe,
266+
}
267+
268+
impl FileDescriptorReservation {
269+
/// Creates a new file descriptor reservation.
270+
pub fn get_unused_fd_flags(flags: u32) -> Result<Self> {
271+
// SAFETY: FFI call, there are no safety requirements on `flags`.
272+
let fd: i32 = unsafe { bindings::get_unused_fd_flags(flags) };
273+
if fd < 0 {
274+
return Err(Error::from_errno(fd));
275+
}
276+
Ok(Self {
277+
fd: fd as u32,
278+
_not_send: NotThreadSafe,
279+
})
280+
}
281+
282+
/// Returns the file descriptor number that was reserved.
283+
pub fn reserved_fd(&self) -> u32 {
284+
self.fd
285+
}
286+
287+
/// Commits the reservation.
288+
///
289+
/// The previously reserved file descriptor is bound to `file`. This method consumes the
290+
/// [`FileDescriptorReservation`], so it will not be usable after this call.
291+
pub fn fd_install(self, file: ARef<File>) {
292+
// SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`. We have not yet used
293+
// the fd, so it is still valid, and `current` still refers to the same task, as this type
294+
// cannot be moved across task boundaries.
295+
//
296+
// Furthermore, the file pointer is guaranteed to own a refcount by its type invariants,
297+
// and we take ownership of that refcount by not running the destructor below.
298+
unsafe { bindings::fd_install(self.fd, file.as_ptr()) };
299+
300+
// `fd_install` consumes both the file descriptor and the file reference, so we cannot run
301+
// the destructors.
302+
core::mem::forget(self);
303+
core::mem::forget(file);
304+
}
305+
}
306+
307+
impl Drop for FileDescriptorReservation {
308+
fn drop(&mut self) {
309+
// SAFETY: By the type invariants of this type, `self.fd` was previously returned by
310+
// `get_unused_fd_flags`. We have not yet used the fd, so it is still valid, and `current`
311+
// still refers to the same task, as this type cannot be moved across task boundaries.
312+
unsafe { bindings::put_unused_fd(self.fd) };
313+
}
314+
}
315+
246316
/// Represents the `EBADF` error code.
247317
///
248318
/// Used for methods that can only fail with `EBADF`.

0 commit comments

Comments
 (0)