|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Files and file descriptors. |
| 4 | +//! |
| 5 | +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) and |
| 6 | +//! [`include/linux/file.h`](srctree/include/linux/file.h) |
| 7 | +
|
| 8 | +use crate::{ |
| 9 | + bindings, |
| 10 | + error::{code::*, Error, Result}, |
| 11 | + types::{ARef, AlwaysRefCounted, Opaque}, |
| 12 | +}; |
| 13 | +use core::ptr; |
| 14 | + |
| 15 | +/// Flags associated with a [`File`]. |
| 16 | +pub mod flags { |
| 17 | + /// File is opened in append mode. |
| 18 | + pub const O_APPEND: u32 = bindings::O_APPEND; |
| 19 | + |
| 20 | + /// Signal-driven I/O is enabled. |
| 21 | + pub const O_ASYNC: u32 = bindings::FASYNC; |
| 22 | + |
| 23 | + /// Close-on-exec flag is set. |
| 24 | + pub const O_CLOEXEC: u32 = bindings::O_CLOEXEC; |
| 25 | + |
| 26 | + /// File was created if it didn't already exist. |
| 27 | + pub const O_CREAT: u32 = bindings::O_CREAT; |
| 28 | + |
| 29 | + /// Direct I/O is enabled for this file. |
| 30 | + pub const O_DIRECT: u32 = bindings::O_DIRECT; |
| 31 | + |
| 32 | + /// File must be a directory. |
| 33 | + pub const O_DIRECTORY: u32 = bindings::O_DIRECTORY; |
| 34 | + |
| 35 | + /// Like [`O_SYNC`] except metadata is not synced. |
| 36 | + pub const O_DSYNC: u32 = bindings::O_DSYNC; |
| 37 | + |
| 38 | + /// Ensure that this file is created with the `open(2)` call. |
| 39 | + pub const O_EXCL: u32 = bindings::O_EXCL; |
| 40 | + |
| 41 | + /// Large file size enabled (`off64_t` over `off_t`). |
| 42 | + pub const O_LARGEFILE: u32 = bindings::O_LARGEFILE; |
| 43 | + |
| 44 | + /// Do not update the file last access time. |
| 45 | + pub const O_NOATIME: u32 = bindings::O_NOATIME; |
| 46 | + |
| 47 | + /// File should not be used as process's controlling terminal. |
| 48 | + pub const O_NOCTTY: u32 = bindings::O_NOCTTY; |
| 49 | + |
| 50 | + /// If basename of path is a symbolic link, fail open. |
| 51 | + pub const O_NOFOLLOW: u32 = bindings::O_NOFOLLOW; |
| 52 | + |
| 53 | + /// File is using nonblocking I/O. |
| 54 | + pub const O_NONBLOCK: u32 = bindings::O_NONBLOCK; |
| 55 | + |
| 56 | + /// File is using nonblocking I/O. |
| 57 | + /// |
| 58 | + /// This is effectively the same flag as [`O_NONBLOCK`] on all architectures |
| 59 | + /// except SPARC64. |
| 60 | + pub const O_NDELAY: u32 = bindings::O_NDELAY; |
| 61 | + |
| 62 | + /// Used to obtain a path file descriptor. |
| 63 | + pub const O_PATH: u32 = bindings::O_PATH; |
| 64 | + |
| 65 | + /// Write operations on this file will flush data and metadata. |
| 66 | + pub const O_SYNC: u32 = bindings::O_SYNC; |
| 67 | + |
| 68 | + /// This file is an unnamed temporary regular file. |
| 69 | + pub const O_TMPFILE: u32 = bindings::O_TMPFILE; |
| 70 | + |
| 71 | + /// File should be truncated to length 0. |
| 72 | + pub const O_TRUNC: u32 = bindings::O_TRUNC; |
| 73 | + |
| 74 | + /// Bitmask for access mode flags. |
| 75 | + /// |
| 76 | + /// # Examples |
| 77 | + /// |
| 78 | + /// ``` |
| 79 | + /// use kernel::file; |
| 80 | + /// # fn do_something() {} |
| 81 | + /// # let flags = 0; |
| 82 | + /// if (flags & file::flags::O_ACCMODE) == file::flags::O_RDONLY { |
| 83 | + /// do_something(); |
| 84 | + /// } |
| 85 | + /// ``` |
| 86 | + pub const O_ACCMODE: u32 = bindings::O_ACCMODE; |
| 87 | + |
| 88 | + /// File is read only. |
| 89 | + pub const O_RDONLY: u32 = bindings::O_RDONLY; |
| 90 | + |
| 91 | + /// File is write only. |
| 92 | + pub const O_WRONLY: u32 = bindings::O_WRONLY; |
| 93 | + |
| 94 | + /// File can be both read and written. |
| 95 | + pub const O_RDWR: u32 = bindings::O_RDWR; |
| 96 | +} |
| 97 | + |
| 98 | +/// Wraps the kernel's `struct file`. |
| 99 | +/// |
| 100 | +/// This represents an open file rather than a file on a filesystem. Processes generally reference |
| 101 | +/// open files using file descriptors. However, file descriptors are not the same as files. A file |
| 102 | +/// descriptor is just an integer that corresponds to a file, and a single file may be referenced |
| 103 | +/// by multiple file descriptors. |
| 104 | +/// |
| 105 | +/// # Refcounting |
| 106 | +/// |
| 107 | +/// Instances of this type are reference-counted. The reference count is incremented by the |
| 108 | +/// `fget`/`get_file` functions and decremented by `fput`. The Rust type `ARef<File>` represents a |
| 109 | +/// pointer that owns a reference count on the file. |
| 110 | +/// |
| 111 | +/// Whenever a process opens a file descriptor (fd), it stores a pointer to the file in its `struct |
| 112 | +/// files_struct`. This pointer owns a reference count to the file, ensuring the file isn't |
| 113 | +/// prematurely deleted while the file descriptor is open. In Rust terminology, the pointers in |
| 114 | +/// `struct files_struct` are `ARef<File>` pointers. |
| 115 | +/// |
| 116 | +/// ## Light refcounts |
| 117 | +/// |
| 118 | +/// Whenever a process has an fd to a file, it may use something called a "light refcount" as a |
| 119 | +/// performance optimization. Light refcounts are acquired by calling `fdget` and released with |
| 120 | +/// `fdput`. The idea behind light refcounts is that if the fd is not closed between the calls to |
| 121 | +/// `fdget` and `fdput`, then the refcount cannot hit zero during that time, as the `struct |
| 122 | +/// files_struct` holds a reference until the fd is closed. This means that it's safe to access the |
| 123 | +/// file even if `fdget` does not increment the refcount. |
| 124 | +/// |
| 125 | +/// The requirement that the fd is not closed during a light refcount applies globally across all |
| 126 | +/// threads - not just on the thread using the light refcount. For this reason, light refcounts are |
| 127 | +/// only used when the `struct files_struct` is not shared with other threads, since this ensures |
| 128 | +/// that other unrelated threads cannot suddenly start using the fd and close it. Therefore, |
| 129 | +/// calling `fdget` on a shared `struct files_struct` creates a normal refcount instead of a light |
| 130 | +/// refcount. |
| 131 | +/// |
| 132 | +/// Light reference counts must be released with `fdput` before the system call returns to |
| 133 | +/// userspace. This means that if you wait until the current system call returns to userspace, then |
| 134 | +/// all light refcounts that existed at the time have gone away. |
| 135 | +/// |
| 136 | +/// ## Rust references |
| 137 | +/// |
| 138 | +/// The reference type `&File` is similar to light refcounts: |
| 139 | +/// |
| 140 | +/// * `&File` references don't own a reference count. They can only exist as long as the reference |
| 141 | +/// count stays positive, and can only be created when there is some mechanism in place to ensure |
| 142 | +/// this. |
| 143 | +/// |
| 144 | +/// * The Rust borrow-checker normally ensures this by enforcing that the `ARef<File>` from which |
| 145 | +/// a `&File` is created outlives the `&File`. |
| 146 | +/// |
| 147 | +/// * Using the unsafe [`File::from_ptr`] means that it is up to the caller to ensure that the |
| 148 | +/// `&File` only exists while the reference count is positive. |
| 149 | +/// |
| 150 | +/// * You can think of `fdget` as using an fd to look up an `ARef<File>` in the `struct |
| 151 | +/// files_struct` and create an `&File` from it. The "fd cannot be closed" rule is like the Rust |
| 152 | +/// rule "the `ARef<File>` must outlive the `&File`". |
| 153 | +/// |
| 154 | +/// # Invariants |
| 155 | +/// |
| 156 | +/// * Instances of this type are refcounted using the `f_count` field. |
| 157 | +/// * If an fd with active light refcounts is closed, then it must be the case that the file |
| 158 | +/// refcount is positive until all light refcounts of the fd have been dropped. |
| 159 | +/// * A light refcount must be dropped before returning to userspace. |
| 160 | +#[repr(transparent)] |
| 161 | +pub struct File(Opaque<bindings::file>); |
| 162 | + |
| 163 | +// SAFETY: |
| 164 | +// - `File::dec_ref` can be called from any thread. |
| 165 | +// - It is okay to send ownership of `struct file` across thread boundaries. |
| 166 | +unsafe impl Send for File {} |
| 167 | + |
| 168 | +// SAFETY: All methods defined on `File` that take `&self` are safe to call even if other threads |
| 169 | +// are concurrently accessing the same `struct file`, because those methods either access immutable |
| 170 | +// properties or have proper synchronization to ensure that such accesses are safe. |
| 171 | +unsafe impl Sync for File {} |
| 172 | + |
| 173 | +impl File { |
| 174 | + /// Constructs a new `struct file` wrapper from a file descriptor. |
| 175 | + /// |
| 176 | + /// The file descriptor belongs to the current process. |
| 177 | + pub fn fget(fd: u32) -> Result<ARef<Self>, BadFdError> { |
| 178 | + // SAFETY: FFI call, there are no requirements on `fd`. |
| 179 | + let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(BadFdError)?; |
| 180 | + |
| 181 | + // SAFETY: `bindings::fget` either returns null or a valid pointer to a file, and we |
| 182 | + // checked for null above. |
| 183 | + // |
| 184 | + // INVARIANT: `bindings::fget` creates a refcount, and we pass ownership of the refcount to |
| 185 | + // the new `ARef<File>`. |
| 186 | + Ok(unsafe { ARef::from_raw(ptr.cast()) }) |
| 187 | + } |
| 188 | + |
| 189 | + /// Creates a reference to a [`File`] from a valid pointer. |
| 190 | + /// |
| 191 | + /// # Safety |
| 192 | + /// |
| 193 | + /// The caller must ensure that `ptr` points at a valid file and that the file's refcount is |
| 194 | + /// positive for the duration of 'a. |
| 195 | + pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { |
| 196 | + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the |
| 197 | + // duration of 'a. The cast is okay because `File` is `repr(transparent)`. |
| 198 | + // |
| 199 | + // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during |
| 200 | + // 'a. |
| 201 | + unsafe { &*ptr.cast() } |
| 202 | + } |
| 203 | + |
| 204 | + /// Returns a raw pointer to the inner C struct. |
| 205 | + #[inline] |
| 206 | + pub fn as_ptr(&self) -> *mut bindings::file { |
| 207 | + self.0.get() |
| 208 | + } |
| 209 | + |
| 210 | + /// Returns the flags associated with the file. |
| 211 | + /// |
| 212 | + /// The flags are a combination of the constants in [`flags`]. |
| 213 | + pub fn flags(&self) -> u32 { |
| 214 | + // This `read_volatile` is intended to correspond to a READ_ONCE call. |
| 215 | + // |
| 216 | + // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount. |
| 217 | + // |
| 218 | + // FIXME(read_once): Replace with `read_once` when available on the Rust side. |
| 219 | + unsafe { core::ptr::addr_of!((*self.as_ptr()).f_flags).read_volatile() } |
| 220 | + } |
| 221 | +} |
| 222 | + |
| 223 | +// SAFETY: The type invariants guarantee that `File` is always ref-counted. This implementation |
| 224 | +// makes `ARef<File>` own a normal refcount. |
| 225 | +unsafe impl AlwaysRefCounted for File { |
| 226 | + fn inc_ref(&self) { |
| 227 | + // SAFETY: The existence of a shared reference means that the refcount is nonzero. |
| 228 | + unsafe { bindings::get_file(self.as_ptr()) }; |
| 229 | + } |
| 230 | + |
| 231 | + unsafe fn dec_ref(obj: ptr::NonNull<File>) { |
| 232 | + // SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we |
| 233 | + // may drop it. The cast is okay since `File` has the same representation as `struct file`. |
| 234 | + unsafe { bindings::fput(obj.cast().as_ptr()) } |
| 235 | + } |
| 236 | +} |
| 237 | + |
| 238 | +/// Represents the `EBADF` error code. |
| 239 | +/// |
| 240 | +/// Used for methods that can only fail with `EBADF`. |
| 241 | +#[derive(Copy, Clone, Eq, PartialEq)] |
| 242 | +pub struct BadFdError; |
| 243 | + |
| 244 | +impl From<BadFdError> for Error { |
| 245 | + fn from(_: BadFdError) -> Error { |
| 246 | + EBADF |
| 247 | + } |
| 248 | +} |
| 249 | + |
| 250 | +impl core::fmt::Debug for BadFdError { |
| 251 | + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 252 | + f.pad("EBADF") |
| 253 | + } |
| 254 | +} |
0 commit comments