Message ID | 20240118-alice-file-v3-1-9694b6f9580c@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | File abstractions needed by Rust Binder | expand |
> +++ b/rust/kernel/file.rs > @@ -0,0 +1,251 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Files and file descriptors. > +//! > +//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and > +//! [`include/linux/file.h`](../../../../include/linux/file.h) > + These could be converted to use Commit bc2e7d5c298a ("rust: support `srctree`-relative links"). Same applies to links in `rust/kernel/cred.rs`, and `rust/kernel/security.rs`. - Valentin
On 18.01.24 15:36, Alice Ryhl wrote: > +/// Wraps the kernel's `struct file`. > +/// > +/// # Refcounting > +/// > +/// Instances of this type are reference-counted. The reference count is incremented by the > +/// `fget`/`get_file` functions and decremented by `fput`. The Rust type `ARef<File>` represents a > +/// pointer that owns a reference count on the file. > +/// > +/// Whenever a process opens a file descriptor (fd), it stores a pointer to the file in its `struct > +/// files_struct`. This pointer owns a reference count to the file, ensuring the file isn't > +/// prematurely deleted while the file descriptor is open. In Rust terminology, the pointers in > +/// `struct files_struct` are `ARef<File>` pointers. > +/// > +/// ## Light refcounts > +/// > +/// Whenever a process has an fd to a file, it may use something called a "light refcount" as a > +/// performance optimization. Light refcounts are acquired by calling `fdget` and released with > +/// `fdput`. The idea behind light refcounts is that if the fd is not closed between the calls to > +/// `fdget` and `fdput`, then the refcount cannot hit zero during that time, as the `struct > +/// files_struct` holds a reference until the fd is closed. This means that it's safe to access the > +/// file even if `fdget` does not increment the refcount. > +/// > +/// The requirement that the fd is not closed during a light refcount applies globally across all > +/// threads - not just on the thread using the light refcount. For this reason, light refcounts are > +/// only used when the `struct files_struct` is not shared with other threads, since this ensures > +/// that other unrelated threads cannot suddenly start using the fd and close it. Therefore, > +/// calling `fdget` on a shared `struct files_struct` creates a normal refcount instead of a light > +/// refcount. > +/// > +/// Light reference counts must be released with `fdput` before the system call returns to > +/// userspace. This means that if you wait until the current system call returns to userspace, then > +/// all light refcounts that existed at the time have gone away. > +/// > +/// ## Rust references > +/// > +/// The reference type `&File` is similar to light refcounts: > +/// > +/// * `&File` references don't own a reference count. They can only exist as long as the reference > +/// count stays positive, and can only be created when there is some mechanism in place to ensure > +/// this. > +/// > +/// * The Rust borrow-checker normally ensures this by enforcing that the `ARef<File>` from which > +/// a `&File` is created outlives the `&File`. > +/// > +/// * Using the unsafe [`File::from_ptr`] means that it is up to the caller to ensure that the > +/// `&File` only exists while the reference count is positive. > +/// > +/// * You can think of `fdget` as using an fd to look up an `ARef<File>` in the `struct > +/// files_struct` and create an `&File` from it. The "fd cannot be closed" rule is like the Rust > +/// rule "the `ARef<File>` must outlive the `&File`". > +/// > +/// # Invariants > +/// > +/// * Instances of this type are refcounted using the `f_count` field. > +/// * If an fd with active light refcounts is closed, then it must be the case that the file > +/// refcount is positive until there are no more light refcounts created from the fd that got I think this wording can be easily misinterpreted: "until there are no more light refcounts created" could mean that you are allowed to drop the refcount to zero after the last light refcount has been created. But in reality you want all light refcounts to be released first. I would suggest "until all light refcounts of the fd have been dropped" or similar. > +/// closed. > +/// * A light refcount must be dropped before returning to userspace. > +#[repr(transparent)] > +pub struct File(Opaque<bindings::file>); > + > +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. > +// This means that the only situation in which a `File` can be accessed mutably is when the > +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so > +// it is ok for this type to be `Send`. Technically, `drop` is never called for `File`, since it is only used via `ARef<File>` which calls `dec_ref` instead. Also since it only contains an `Opaque`, dropping it is a noop. But what does `Send` mean for this type? Since it is used together with `ARef`, being `Send` means that `File::dec_ref` can be called from any thread. I think we are missing this as a safety requirement on `AlwaysRefCounted`, do you agree? I think the safety justification here could be (with the requirement added to `AlwaysRefCounted`): SAFETY: - `File::drop` can be called from any thread. - `File::dec_ref` can be called from any thread. -- Cheers, Benno > +unsafe impl Send for File {} > + > +// SAFETY: All methods defined on `File` that take `&self` are safe to call even if other threads > +// are concurrently accessing the same `struct file`, because those methods either access immutable > +// properties or have proper synchronization to ensure that such accesses are safe. > +unsafe impl Sync for File {}
On Fri, Jan 26, 2024 at 4:04 PM Benno Lossin <benno.lossin@proton.me> wrote: > > On 18.01.24 15:36, Alice Ryhl wrote: > > +/// Wraps the kernel's `struct file`. > > +/// > > +/// # Refcounting > > +/// > > +/// Instances of this type are reference-counted. The reference count is incremented by the > > +/// `fget`/`get_file` functions and decremented by `fput`. The Rust type `ARef<File>` represents a > > +/// pointer that owns a reference count on the file. > > +/// > > +/// Whenever a process opens a file descriptor (fd), it stores a pointer to the file in its `struct > > +/// files_struct`. This pointer owns a reference count to the file, ensuring the file isn't > > +/// prematurely deleted while the file descriptor is open. In Rust terminology, the pointers in > > +/// `struct files_struct` are `ARef<File>` pointers. > > +/// > > +/// ## Light refcounts > > +/// > > +/// Whenever a process has an fd to a file, it may use something called a "light refcount" as a > > +/// performance optimization. Light refcounts are acquired by calling `fdget` and released with > > +/// `fdput`. The idea behind light refcounts is that if the fd is not closed between the calls to > > +/// `fdget` and `fdput`, then the refcount cannot hit zero during that time, as the `struct > > +/// files_struct` holds a reference until the fd is closed. This means that it's safe to access the > > +/// file even if `fdget` does not increment the refcount. > > +/// > > +/// The requirement that the fd is not closed during a light refcount applies globally across all > > +/// threads - not just on the thread using the light refcount. For this reason, light refcounts are > > +/// only used when the `struct files_struct` is not shared with other threads, since this ensures > > +/// that other unrelated threads cannot suddenly start using the fd and close it. Therefore, > > +/// calling `fdget` on a shared `struct files_struct` creates a normal refcount instead of a light > > +/// refcount. > > +/// > > +/// Light reference counts must be released with `fdput` before the system call returns to > > +/// userspace. This means that if you wait until the current system call returns to userspace, then > > +/// all light refcounts that existed at the time have gone away. > > +/// > > +/// ## Rust references > > +/// > > +/// The reference type `&File` is similar to light refcounts: > > +/// > > +/// * `&File` references don't own a reference count. They can only exist as long as the reference > > +/// count stays positive, and can only be created when there is some mechanism in place to ensure > > +/// this. > > +/// > > +/// * The Rust borrow-checker normally ensures this by enforcing that the `ARef<File>` from which > > +/// a `&File` is created outlives the `&File`. > > +/// > > +/// * Using the unsafe [`File::from_ptr`] means that it is up to the caller to ensure that the > > +/// `&File` only exists while the reference count is positive. > > +/// > > +/// * You can think of `fdget` as using an fd to look up an `ARef<File>` in the `struct > > +/// files_struct` and create an `&File` from it. The "fd cannot be closed" rule is like the Rust > > +/// rule "the `ARef<File>` must outlive the `&File`". > > +/// > > +/// # Invariants > > +/// > > +/// * Instances of this type are refcounted using the `f_count` field. > > +/// * If an fd with active light refcounts is closed, then it must be the case that the file > > +/// refcount is positive until there are no more light refcounts created from the fd that got > > I think this wording can be easily misinterpreted: "until there > are no more light refcounts created" could mean that you are allowed > to drop the refcount to zero after the last light refcount has been > created. But in reality you want all light refcounts to be released > first. > I would suggest "until all light refcounts of the fd have been dropped" > or similar. Will do. > > +/// closed. > > +/// * A light refcount must be dropped before returning to userspace. > > +#[repr(transparent)] > > +pub struct File(Opaque<bindings::file>); > > + > > +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. > > +// This means that the only situation in which a `File` can be accessed mutably is when the > > +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so > > +// it is ok for this type to be `Send`. > > Technically, `drop` is never called for `File`, since it is only used > via `ARef<File>` which calls `dec_ref` instead. Also since it only contains > an `Opaque`, dropping it is a noop. > But what does `Send` mean for this type? Since it is used together with > `ARef`, being `Send` means that `File::dec_ref` can be called from any > thread. I think we are missing this as a safety requirement on > `AlwaysRefCounted`, do you agree? > I think the safety justification here could be (with the requirement added > to `AlwaysRefCounted`): > > SAFETY: > - `File::drop` can be called from any thread. > - `File::dec_ref` can be called from any thread. This wording was taken from rust/kernel/task.rs. I think it's out of scope to reword it. Besides, it says "destructor runs", not "drop runs". The destructor can be interpreted to mean the right thing for ARef. The right safety comment would probably be that dec_ref can be called from any thread. Alice
On 29.01.24 17:34, Alice Ryhl wrote: > On Fri, Jan 26, 2024 at 4:04 PM Benno Lossin <benno.lossin@proton.me> wrote: >>> +/// closed. >>> +/// * A light refcount must be dropped before returning to userspace. >>> +#[repr(transparent)] >>> +pub struct File(Opaque<bindings::file>); >>> + >>> +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. >>> +// This means that the only situation in which a `File` can be accessed mutably is when the >>> +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so >>> +// it is ok for this type to be `Send`. >> >> Technically, `drop` is never called for `File`, since it is only used >> via `ARef<File>` which calls `dec_ref` instead. Also since it only contains >> an `Opaque`, dropping it is a noop. >> But what does `Send` mean for this type? Since it is used together with >> `ARef`, being `Send` means that `File::dec_ref` can be called from any >> thread. I think we are missing this as a safety requirement on >> `AlwaysRefCounted`, do you agree? >> I think the safety justification here could be (with the requirement added >> to `AlwaysRefCounted`): >> >> SAFETY: >> - `File::drop` can be called from any thread. >> - `File::dec_ref` can be called from any thread. > > This wording was taken from rust/kernel/task.rs. I think it's out of > scope to reword it. Rewording the safety docs on `AlwaysRefCounted`, yes that is out of scope, I was just checking if you agree that the current wording is incomplete. > Besides, it says "destructor runs", not "drop runs". The destructor > can be interpreted to mean the right thing for ARef. To me "destructor runs" and "drop runs" are synonyms. > The right safety comment would probably be that dec_ref can be called > from any thread. Yes and no, I would prefer if you could remove the "By design, ..." part and only focus on `dec_ref` being callable from any thread and it being ok to send a `File` to a different thread.
On Thu, Feb 1, 2024 at 10:31 AM Benno Lossin <benno.lossin@proton.me> wrote: > > On 29.01.24 17:34, Alice Ryhl wrote: > > On Fri, Jan 26, 2024 at 4:04 PM Benno Lossin <benno.lossin@proton.me> wrote: > >>> +/// closed. > >>> +/// * A light refcount must be dropped before returning to userspace. > >>> +#[repr(transparent)] > >>> +pub struct File(Opaque<bindings::file>); > >>> + > >>> +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. > >>> +// This means that the only situation in which a `File` can be accessed mutably is when the > >>> +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so > >>> +// it is ok for this type to be `Send`. > >> > >> Technically, `drop` is never called for `File`, since it is only used > >> via `ARef<File>` which calls `dec_ref` instead. Also since it only contains > >> an `Opaque`, dropping it is a noop. > >> But what does `Send` mean for this type? Since it is used together with > >> `ARef`, being `Send` means that `File::dec_ref` can be called from any > >> thread. I think we are missing this as a safety requirement on > >> `AlwaysRefCounted`, do you agree? > >> I think the safety justification here could be (with the requirement added > >> to `AlwaysRefCounted`): > >> > >> SAFETY: > >> - `File::drop` can be called from any thread. > >> - `File::dec_ref` can be called from any thread. > > > > This wording was taken from rust/kernel/task.rs. I think it's out of > > scope to reword it. > > Rewording the safety docs on `AlwaysRefCounted`, yes that is out of scope, > I was just checking if you agree that the current wording is incomplete. That's not what I meant. The wording of this safety comment is identical to the wording in other existing safety comments in the kernel, such as e.g. the one for `impl Send for Task`. > > Besides, it says "destructor runs", not "drop runs". The destructor > > can be interpreted to mean the right thing for ARef. > > To me "destructor runs" and "drop runs" are synonyms. > > > The right safety comment would probably be that dec_ref can be called > > from any thread. > > Yes and no, I would prefer if you could remove the "By design, ..." > part and only focus on `dec_ref` being callable from any thread and > it being ok to send a `File` to a different thread.
On 01.02.24 10:33, Alice Ryhl wrote: > On Thu, Feb 1, 2024 at 10:31 AM Benno Lossin <benno.lossin@proton.me> wrote: >> >> On 29.01.24 17:34, Alice Ryhl wrote: >>> On Fri, Jan 26, 2024 at 4:04 PM Benno Lossin <benno.lossin@proton.me> wrote: >>>>> +/// closed. >>>>> +/// * A light refcount must be dropped before returning to userspace. >>>>> +#[repr(transparent)] >>>>> +pub struct File(Opaque<bindings::file>); >>>>> + >>>>> +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. >>>>> +// This means that the only situation in which a `File` can be accessed mutably is when the >>>>> +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so >>>>> +// it is ok for this type to be `Send`. >>>> >>>> Technically, `drop` is never called for `File`, since it is only used >>>> via `ARef<File>` which calls `dec_ref` instead. Also since it only contains >>>> an `Opaque`, dropping it is a noop. >>>> But what does `Send` mean for this type? Since it is used together with >>>> `ARef`, being `Send` means that `File::dec_ref` can be called from any >>>> thread. I think we are missing this as a safety requirement on >>>> `AlwaysRefCounted`, do you agree? >>>> I think the safety justification here could be (with the requirement added >>>> to `AlwaysRefCounted`): >>>> >>>> SAFETY: >>>> - `File::drop` can be called from any thread. >>>> - `File::dec_ref` can be called from any thread. >>> >>> This wording was taken from rust/kernel/task.rs. I think it's out of >>> scope to reword it. >> >> Rewording the safety docs on `AlwaysRefCounted`, yes that is out of scope, >> I was just checking if you agree that the current wording is incomplete. > > That's not what I meant. The wording of this safety comment is > identical to the wording in other existing safety comments in the > kernel, such as e.g. the one for `impl Send for Task`. Ah I see. But I still think changing it is better, since it would only get shorter. The comment on `Task` can be fixed later. Or do you want to keep consistency here? Because I would prefer to make this right and then change `Task` later.
On Thu, Feb 1, 2024 at 10:38 AM Benno Lossin <benno.lossin@proton.me> wrote: > > On 01.02.24 10:33, Alice Ryhl wrote: > > On Thu, Feb 1, 2024 at 10:31 AM Benno Lossin <benno.lossin@proton.me> wrote: > >> > >> On 29.01.24 17:34, Alice Ryhl wrote: > >>> On Fri, Jan 26, 2024 at 4:04 PM Benno Lossin <benno.lossin@proton.me> wrote: > >>>>> +/// closed. > >>>>> +/// * A light refcount must be dropped before returning to userspace. > >>>>> +#[repr(transparent)] > >>>>> +pub struct File(Opaque<bindings::file>); > >>>>> + > >>>>> +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. > >>>>> +// This means that the only situation in which a `File` can be accessed mutably is when the > >>>>> +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so > >>>>> +// it is ok for this type to be `Send`. > >>>> > >>>> Technically, `drop` is never called for `File`, since it is only used > >>>> via `ARef<File>` which calls `dec_ref` instead. Also since it only contains > >>>> an `Opaque`, dropping it is a noop. > >>>> But what does `Send` mean for this type? Since it is used together with > >>>> `ARef`, being `Send` means that `File::dec_ref` can be called from any > >>>> thread. I think we are missing this as a safety requirement on > >>>> `AlwaysRefCounted`, do you agree? > >>>> I think the safety justification here could be (with the requirement added > >>>> to `AlwaysRefCounted`): > >>>> > >>>> SAFETY: > >>>> - `File::drop` can be called from any thread. > >>>> - `File::dec_ref` can be called from any thread. > >>> > >>> This wording was taken from rust/kernel/task.rs. I think it's out of > >>> scope to reword it. > >> > >> Rewording the safety docs on `AlwaysRefCounted`, yes that is out of scope, > >> I was just checking if you agree that the current wording is incomplete. > > > > That's not what I meant. The wording of this safety comment is > > identical to the wording in other existing safety comments in the > > kernel, such as e.g. the one for `impl Send for Task`. > > Ah I see. But I still think changing it is better, since it would only get > shorter. The comment on `Task` can be fixed later. > Or do you want to keep consistency here? Because I would prefer to make > this right and then change `Task` later. What would you like me to change it to? For example: // SAFETY: It is okay to send references to a File across thread boundaries. Alice
On 01.02.24 10:41, Alice Ryhl wrote: > On Thu, Feb 1, 2024 at 10:38 AM Benno Lossin <benno.lossin@proton.me> wrote: >> >> On 01.02.24 10:33, Alice Ryhl wrote: >>> On Thu, Feb 1, 2024 at 10:31 AM Benno Lossin <benno.lossin@proton.me> wrote: >>>> >>>> On 29.01.24 17:34, Alice Ryhl wrote: >>>>> On Fri, Jan 26, 2024 at 4:04 PM Benno Lossin <benno.lossin@proton.me> wrote: >>>>>>> +/// closed. >>>>>>> +/// * A light refcount must be dropped before returning to userspace. >>>>>>> +#[repr(transparent)] >>>>>>> +pub struct File(Opaque<bindings::file>); >>>>>>> + >>>>>>> +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. >>>>>>> +// This means that the only situation in which a `File` can be accessed mutably is when the >>>>>>> +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so >>>>>>> +// it is ok for this type to be `Send`. >>>>>> >>>>>> Technically, `drop` is never called for `File`, since it is only used >>>>>> via `ARef<File>` which calls `dec_ref` instead. Also since it only contains >>>>>> an `Opaque`, dropping it is a noop. >>>>>> But what does `Send` mean for this type? Since it is used together with >>>>>> `ARef`, being `Send` means that `File::dec_ref` can be called from any >>>>>> thread. I think we are missing this as a safety requirement on >>>>>> `AlwaysRefCounted`, do you agree? >>>>>> I think the safety justification here could be (with the requirement added >>>>>> to `AlwaysRefCounted`): >>>>>> >>>>>> SAFETY: >>>>>> - `File::drop` can be called from any thread. >>>>>> - `File::dec_ref` can be called from any thread. >>>>> >>>>> This wording was taken from rust/kernel/task.rs. I think it's out of >>>>> scope to reword it. >>>> >>>> Rewording the safety docs on `AlwaysRefCounted`, yes that is out of scope, >>>> I was just checking if you agree that the current wording is incomplete. >>> >>> That's not what I meant. The wording of this safety comment is >>> identical to the wording in other existing safety comments in the >>> kernel, such as e.g. the one for `impl Send for Task`. >> >> Ah I see. But I still think changing it is better, since it would only get >> shorter. The comment on `Task` can be fixed later. >> Or do you want to keep consistency here? Because I would prefer to make >> this right and then change `Task` later. > > What would you like me to change it to? > > For example: > // SAFETY: It is okay to send references to a File across thread boundaries. That would fit better as the safety comment for `Sync`, since it refers to "references". For `Send` I think this would be better: // SAFETY: // - `File::dec_ref` can be called from any thread. // - It is okay to send ownership of `File` across thread boundaries.
diff --git a/fs/file.c b/fs/file.c index 5fb0b146e79e..b69b2b1316f7 100644 --- a/fs/file.c +++ b/fs/file.c @@ -1101,18 +1101,25 @@ EXPORT_SYMBOL(task_lookup_next_fdget_rcu); /* * Lightweight file lookup - no refcnt increment if fd table isn't shared. * * You can use this instead of fget if you satisfy all of the following * conditions: * 1) You must call fput_light before exiting the syscall and returning control * to userspace (i.e. you cannot remember the returned struct file * after * returning to userspace). * 2) You must not call filp_close on the returned struct file * in between * calls to fget_light and fput_light. * 3) You must not clone the current task in between the calls to fget_light * and fput_light. * * The fput_needed flag returned by fget_light should be passed to the * corresponding fput_light. + * + * (As an exception to rule 2, you can call filp_close between fget_light and + * fput_light provided that you capture a real refcount with get_file before + * the call to filp_close, and ensure that this real refcount is fput *after* + * the fput_light call.) + * + * See also the documentation in rust/kernel/file.rs. */ static unsigned long __fget_light(unsigned int fd, fmode_t mask) { diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index b5714fb69fe3..ed06970d789a 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -8,6 +8,8 @@ #include <kunit/test.h> #include <linux/errname.h> +#include <linux/file.h> +#include <linux/fs.h> #include <linux/slab.h> #include <linux/refcount.h> #include <linux/wait.h> diff --git a/rust/helpers.c b/rust/helpers.c index 70e59efd92bc..03141a3608a4 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -25,6 +25,7 @@ #include <linux/build_bug.h> #include <linux/err.h> #include <linux/errname.h> +#include <linux/fs.h> #include <linux/mutex.h> #include <linux/refcount.h> #include <linux/sched/signal.h> @@ -157,6 +158,12 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, } EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); +struct file *rust_helper_get_file(struct file *f) +{ + return get_file(f); +} +EXPORT_SYMBOL_GPL(rust_helper_get_file); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs new file mode 100644 index 000000000000..b7ded0cdd063 --- /dev/null +++ b/rust/kernel/file.rs @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Files and file descriptors. +//! +//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and +//! [`include/linux/file.h`](../../../../include/linux/file.h) + +use crate::{ + bindings, + error::{code::*, Error, Result}, + types::{ARef, AlwaysRefCounted, Opaque}, +}; +use core::ptr; + +/// Flags associated with a [`File`]. +pub mod flags { + /// File is opened in append mode. + pub const O_APPEND: u32 = bindings::O_APPEND; + + /// Signal-driven I/O is enabled. + pub const O_ASYNC: u32 = bindings::FASYNC; + + /// Close-on-exec flag is set. + pub const O_CLOEXEC: u32 = bindings::O_CLOEXEC; + + /// File was created if it didn't already exist. + pub const O_CREAT: u32 = bindings::O_CREAT; + + /// Direct I/O is enabled for this file. + pub const O_DIRECT: u32 = bindings::O_DIRECT; + + /// File must be a directory. + pub const O_DIRECTORY: u32 = bindings::O_DIRECTORY; + + /// Like [`O_SYNC`] except metadata is not synced. + pub const O_DSYNC: u32 = bindings::O_DSYNC; + + /// Ensure that this file is created with the `open(2)` call. + pub const O_EXCL: u32 = bindings::O_EXCL; + + /// Large file size enabled (`off64_t` over `off_t`). + pub const O_LARGEFILE: u32 = bindings::O_LARGEFILE; + + /// Do not update the file last access time. + pub const O_NOATIME: u32 = bindings::O_NOATIME; + + /// File should not be used as process's controlling terminal. + pub const O_NOCTTY: u32 = bindings::O_NOCTTY; + + /// If basename of path is a symbolic link, fail open. + pub const O_NOFOLLOW: u32 = bindings::O_NOFOLLOW; + + /// File is using nonblocking I/O. + pub const O_NONBLOCK: u32 = bindings::O_NONBLOCK; + + /// Also known as `O_NDELAY`. + /// + /// This is effectively the same flag as [`O_NONBLOCK`] on all architectures + /// except SPARC64. + pub const O_NDELAY: u32 = bindings::O_NDELAY; + + /// Used to obtain a path file descriptor. + pub const O_PATH: u32 = bindings::O_PATH; + + /// Write operations on this file will flush data and metadata. + pub const O_SYNC: u32 = bindings::O_SYNC; + + /// This file is an unnamed temporary regular file. + pub const O_TMPFILE: u32 = bindings::O_TMPFILE; + + /// File should be truncated to length 0. + pub const O_TRUNC: u32 = bindings::O_TRUNC; + + /// Bitmask for access mode flags. + /// + /// # Examples + /// + /// ``` + /// use kernel::file; + /// # fn do_something() {} + /// # let flags = 0; + /// if (flags & file::flags::O_ACCMODE) == file::flags::O_RDONLY { + /// do_something(); + /// } + /// ``` + pub const O_ACCMODE: u32 = bindings::O_ACCMODE; + + /// File is read only. + pub const O_RDONLY: u32 = bindings::O_RDONLY; + + /// File is write only. + pub const O_WRONLY: u32 = bindings::O_WRONLY; + + /// File can be both read and written. + pub const O_RDWR: u32 = bindings::O_RDWR; +} + +/// Wraps the kernel's `struct file`. +/// +/// # Refcounting +/// +/// Instances of this type are reference-counted. The reference count is incremented by the +/// `fget`/`get_file` functions and decremented by `fput`. The Rust type `ARef<File>` represents a +/// pointer that owns a reference count on the file. +/// +/// Whenever a process opens a file descriptor (fd), it stores a pointer to the file in its `struct +/// files_struct`. This pointer owns a reference count to the file, ensuring the file isn't +/// prematurely deleted while the file descriptor is open. In Rust terminology, the pointers in +/// `struct files_struct` are `ARef<File>` pointers. +/// +/// ## Light refcounts +/// +/// Whenever a process has an fd to a file, it may use something called a "light refcount" as a +/// performance optimization. Light refcounts are acquired by calling `fdget` and released with +/// `fdput`. The idea behind light refcounts is that if the fd is not closed between the calls to +/// `fdget` and `fdput`, then the refcount cannot hit zero during that time, as the `struct +/// files_struct` holds a reference until the fd is closed. This means that it's safe to access the +/// file even if `fdget` does not increment the refcount. +/// +/// The requirement that the fd is not closed during a light refcount applies globally across all +/// threads - not just on the thread using the light refcount. For this reason, light refcounts are +/// only used when the `struct files_struct` is not shared with other threads, since this ensures +/// that other unrelated threads cannot suddenly start using the fd and close it. Therefore, +/// calling `fdget` on a shared `struct files_struct` creates a normal refcount instead of a light +/// refcount. +/// +/// Light reference counts must be released with `fdput` before the system call returns to +/// userspace. This means that if you wait until the current system call returns to userspace, then +/// all light refcounts that existed at the time have gone away. +/// +/// ## Rust references +/// +/// The reference type `&File` is similar to light refcounts: +/// +/// * `&File` references don't own a reference count. They can only exist as long as the reference +/// count stays positive, and can only be created when there is some mechanism in place to ensure +/// this. +/// +/// * The Rust borrow-checker normally ensures this by enforcing that the `ARef<File>` from which +/// a `&File` is created outlives the `&File`. +/// +/// * Using the unsafe [`File::from_ptr`] means that it is up to the caller to ensure that the +/// `&File` only exists while the reference count is positive. +/// +/// * You can think of `fdget` as using an fd to look up an `ARef<File>` in the `struct +/// files_struct` and create an `&File` from it. The "fd cannot be closed" rule is like the Rust +/// rule "the `ARef<File>` must outlive the `&File`". +/// +/// # Invariants +/// +/// * Instances of this type are refcounted using the `f_count` field. +/// * If an fd with active light refcounts is closed, then it must be the case that the file +/// refcount is positive until there are no more light refcounts created from the fd that got +/// closed. +/// * A light refcount must be dropped before returning to userspace. +#[repr(transparent)] +pub struct File(Opaque<bindings::file>); + +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. +// This means that the only situation in which a `File` can be accessed mutably is when the +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so +// it is ok for this type to be `Send`. +unsafe impl Send for File {} + +// SAFETY: All methods defined on `File` that take `&self` are safe to call even if other threads +// are concurrently accessing the same `struct file`, because those methods either access immutable +// properties or have proper synchronization to ensure that such accesses are safe. +unsafe impl Sync for File {} + +impl File { + /// Constructs a new `struct file` wrapper from a file descriptor. + /// + /// The file descriptor belongs to the current process. + pub fn fget(fd: u32) -> Result<ARef<Self>, BadFdError> { + // SAFETY: FFI call, there are no requirements on `fd`. + let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(BadFdError)?; + + // SAFETY: `bindings::fget` either returns null or a valid pointer to a file, and we + // checked for null above. + // + // INVARIANT: `bindings::fget` creates a refcount, and we pass ownership of the refcount to + // the new `ARef<File>`. + Ok(unsafe { ARef::from_raw(ptr.cast()) }) + } + + /// Creates a reference to a [`File`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` points at a valid file and that the file's refcount is + /// positive for the duration of 'a. + pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the + // duration of 'a. The cast is okay because `File` is `repr(transparent)`. + // + // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during + // 'a. + unsafe { &*ptr.cast() } + } + + /// Returns a raw pointer to the inner C struct. + #[inline] + pub fn as_ptr(&self) -> *mut bindings::file { + self.0.get() + } + + /// Returns the flags associated with the file. + /// + /// The flags are a combination of the constants in [`flags`]. + pub fn flags(&self) -> u32 { + // This `read_volatile` is intended to correspond to a READ_ONCE call. + // + // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount. + // + // TODO: Replace with `read_once` when available on the Rust side. + unsafe { core::ptr::addr_of!((*self.as_ptr()).f_flags).read_volatile() } + } +} + +// SAFETY: The type invariants guarantee that `File` is always ref-counted. This implementation +// makes `ARef<File>` own a normal refcount. +unsafe impl AlwaysRefCounted for File { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_file(self.as_ptr()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull<File>) { + // SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we + // may drop it. The cast is okay since `File` has the same representation as `struct file`. + unsafe { bindings::fput(obj.cast().as_ptr()) } + } +} + +/// Represents the `EBADF` error code. +/// +/// Used for methods that can only fail with `EBADF`. +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct BadFdError; + +impl From<BadFdError> for Error { + fn from(_: BadFdError) -> Error { + EBADF + } +} + +impl core::fmt::Debug for BadFdError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.pad("EBADF") + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e6aff80b521f..ce9abceab784 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,7 @@ mod allocator; mod build_assert; pub mod error; +pub mod file; pub mod init; pub mod ioctl; #[cfg(CONFIG_KUNIT)]