|
| 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::{marker::PhantomData, 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 | +/// Compile-time information for keeping track of how a [`File`] is shared. |
| 99 | +/// |
| 100 | +/// The `fdget_pos` method can be used to access the file's position without taking `f_pos_lock`, |
| 101 | +/// as long as the file is not shared with any other threads. During such calls to `fdget_pos`, the |
| 102 | +/// file must remain non-shared, so it must not be possible to move the file to another thread. For |
| 103 | +/// example, if the file is moved to another thread, then it could be passed to `fd_install`, at |
| 104 | +/// which point the remote process could touch the file position. |
| 105 | +/// |
| 106 | +/// The share mode only keeps track of whether there are active `fdget_pos` calls that did not take |
| 107 | +/// the `f_pos_lock`, and does not keep track of `fdget` calls. This is okay because `fdget` does |
| 108 | +/// not care about the refcount of the underlying `struct file`; as long as the entry in the |
| 109 | +/// current thread's fd table does not get removed, it's okay to share the file. For example, |
| 110 | +/// `fd_install`ing the `struct file` into another process is okay during an `fdget` call, because |
| 111 | +/// the other process can't touch the fd table of the original process. |
| 112 | +mod share_mode { |
| 113 | + /// Trait implemented by the two sharing modes that a file might have. |
| 114 | + pub trait FileShareMode {} |
| 115 | + |
| 116 | + /// Represents a file for which there might be an active call to `fdget_pos` that did not take |
| 117 | + /// the `f_pos_lock` lock. |
| 118 | + pub enum MaybeFdgetPos {} |
| 119 | + |
| 120 | + /// Represents a file for which it is known that all active calls to `fdget_pos` (if any) took |
| 121 | + /// the `f_pos_lock` lock. |
| 122 | + pub enum NoFdgetPos {} |
| 123 | + |
| 124 | + impl FileShareMode for MaybeFdgetPos {} |
| 125 | + impl FileShareMode for NoFdgetPos {} |
| 126 | +} |
| 127 | +pub use self::share_mode::{FileShareMode, MaybeFdgetPos, NoFdgetPos}; |
| 128 | + |
| 129 | +/// Wraps the kernel's `struct file`. |
| 130 | +/// |
| 131 | +/// This represents an open file rather than a file on a filesystem. Processes generally reference |
| 132 | +/// open files using file descriptors. However, file descriptors are not the same as files. A file |
| 133 | +/// descriptor is just an integer that corresponds to a file, and a single file may be referenced |
| 134 | +/// by multiple file descriptors. |
| 135 | +/// |
| 136 | +/// # Refcounting |
| 137 | +/// |
| 138 | +/// Instances of this type are reference-counted. The reference count is incremented by the |
| 139 | +/// `fget`/`get_file` functions and decremented by `fput`. The Rust type `ARef<File>` represents a |
| 140 | +/// pointer that owns a reference count on the file. |
| 141 | +/// |
| 142 | +/// Whenever a process opens a file descriptor (fd), it stores a pointer to the file in its fd |
| 143 | +/// table (`struct files_struct`). This pointer owns a reference count to the file, ensuring the |
| 144 | +/// file isn't prematurely deleted while the file descriptor is open. In Rust terminology, the |
| 145 | +/// pointers in `struct files_struct` are `ARef<File>` pointers. |
| 146 | +/// |
| 147 | +/// ## Light refcounts |
| 148 | +/// |
| 149 | +/// Whenever a process has an fd to a file, it may use something called a "light refcount" as a |
| 150 | +/// performance optimization. Light refcounts are acquired by calling `fdget` and released with |
| 151 | +/// `fdput`. The idea behind light refcounts is that if the fd is not closed between the calls to |
| 152 | +/// `fdget` and `fdput`, then the refcount cannot hit zero during that time, as the `struct |
| 153 | +/// files_struct` holds a reference until the fd is closed. This means that it's safe to access the |
| 154 | +/// file even if `fdget` does not increment the refcount. |
| 155 | +/// |
| 156 | +/// The requirement that the fd is not closed during a light refcount applies globally across all |
| 157 | +/// threads - not just on the thread using the light refcount. For this reason, light refcounts are |
| 158 | +/// only used when the `struct files_struct` is not shared with other threads, since this ensures |
| 159 | +/// that other unrelated threads cannot suddenly start using the fd and close it. Therefore, |
| 160 | +/// calling `fdget` on a shared `struct files_struct` creates a normal refcount instead of a light |
| 161 | +/// refcount. |
| 162 | +/// |
| 163 | +/// Light reference counts must be released with `fdput` before the system call returns to |
| 164 | +/// userspace. This means that if you wait until the current system call returns to userspace, then |
| 165 | +/// all light refcounts that existed at the time have gone away. |
| 166 | +/// |
| 167 | +/// ### The file position |
| 168 | +/// |
| 169 | +/// Each `struct file` has a position integer, which is protected by the `f_pos_lock` mutex. |
| 170 | +/// However, if the `struct file` is not shared, then the kernel may avoid taking the lock as a |
| 171 | +/// performance optimization. |
| 172 | +/// |
| 173 | +/// The condition for avoiding the `f_pos_lock` mutex is different from the condition for using |
| 174 | +/// `fdget`. With `fdget`, you may avoid incrementing the refcount as long as the current fd table |
| 175 | +/// is not shared; it is okay if there are other fd tables that also reference the same `struct |
| 176 | +/// file`. However, `fdget_pos` can only avoid taking the `f_pos_lock` if the entire `struct file` |
| 177 | +/// is not shared, as different processes with an fd to the same `struct file` share the same |
| 178 | +/// position. |
| 179 | +/// |
| 180 | +/// ## Rust references |
| 181 | +/// |
| 182 | +/// The reference type `&File` is similar to light refcounts: |
| 183 | +/// |
| 184 | +/// * `&File` references don't own a reference count. They can only exist as long as the reference |
| 185 | +/// count stays positive, and can only be created when there is some mechanism in place to ensure |
| 186 | +/// this. |
| 187 | +/// |
| 188 | +/// * The Rust borrow-checker normally ensures this by enforcing that the `ARef<File>` from which |
| 189 | +/// a `&File` is created outlives the `&File`. |
| 190 | +/// |
| 191 | +/// * Using the unsafe [`File::from_ptr`] means that it is up to the caller to ensure that the |
| 192 | +/// `&File` only exists while the reference count is positive. |
| 193 | +/// |
| 194 | +/// * You can think of `fdget` as using an fd to look up an `ARef<File>` in the `struct |
| 195 | +/// files_struct` and create an `&File` from it. The "fd cannot be closed" rule is like the Rust |
| 196 | +/// rule "the `ARef<File>` must outlive the `&File`". |
| 197 | +/// |
| 198 | +/// # Invariants |
| 199 | +/// |
| 200 | +/// * All instances of this type are refcounted using the `f_count` field. |
| 201 | +/// * If the file sharing mode is `MaybeFdgetPos`, then all active calls to `fdget_pos` that did |
| 202 | +/// not take the `f_pos_lock` mutex must be on the same thread as this `File`. |
| 203 | +/// * If the file sharing mode is `NoFdgetPos`, then there must not be active calls to `fdget_pos` |
| 204 | +/// that did not take the `f_pos_lock` mutex. |
| 205 | +#[repr(transparent)] |
| 206 | +pub struct File<S: FileShareMode> { |
| 207 | + inner: Opaque<bindings::file>, |
| 208 | + _share_mode: PhantomData<S>, |
| 209 | +} |
| 210 | + |
| 211 | +// SAFETY: This file is known to not have any local active `fdget_pos` calls, so it is safe to |
| 212 | +// transfer it between threads. |
| 213 | +unsafe impl Send for File<NoFdgetPos> {} |
| 214 | + |
| 215 | +// SAFETY: This file is known to not have any local active `fdget_pos` calls, so it is safe to |
| 216 | +// access its methods from several threads in parallel. |
| 217 | +unsafe impl Sync for File<NoFdgetPos> {} |
| 218 | + |
| 219 | +/// File methods that only exist under the [`MaybeFdgetPos`] sharing mode. |
| 220 | +impl File<MaybeFdgetPos> { |
| 221 | + /// Constructs a new `struct file` wrapper from a file descriptor. |
| 222 | + /// |
| 223 | + /// The file descriptor belongs to the current process, and there might be active local calls |
| 224 | + /// to `fdget_pos`. |
| 225 | + pub fn fget(fd: u32) -> Result<ARef<File<MaybeFdgetPos>>, BadFdError> { |
| 226 | + // SAFETY: FFI call, there are no requirements on `fd`. |
| 227 | + let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(BadFdError)?; |
| 228 | + |
| 229 | + // SAFETY: `bindings::fget` created a refcount, and we pass ownership of it to the `ARef`. |
| 230 | + // |
| 231 | + // INVARIANT: This file is in the fd table on this thread, so either all `fdget_pos` calls |
| 232 | + // are on this thread, or the file is shared, in which case `fdget_pos` calls took the |
| 233 | + // `f_pos_lock` mutex. |
| 234 | + Ok(unsafe { ARef::from_raw(ptr.cast()) }) |
| 235 | + } |
| 236 | + |
| 237 | + /// Assume that there are no active `fdget_pos` calls that prevent us from sharing this file. |
| 238 | + /// |
| 239 | + /// This makes it safe to transfer this file to other threads. No checks are performed, and |
| 240 | + /// using it incorrectly may lead to a data race on the file position if the file is shared |
| 241 | + /// with another thread. |
| 242 | + /// |
| 243 | + /// This method is intended to be used together with [`File::fget`] when the caller knows |
| 244 | + /// statically that there are no `fdget_pos` calls on the current thread. For example, you |
| 245 | + /// might use it when calling `fget` from an ioctl, since ioctls usually do not touch the file |
| 246 | + /// position. |
| 247 | + /// |
| 248 | + /// # Safety |
| 249 | + /// |
| 250 | + /// There must not be any active `fdget_pos` calls on the current thread. |
| 251 | + pub unsafe fn assume_no_fdget_pos(me: ARef<Self>) -> ARef<File<NoFdgetPos>> { |
| 252 | + // INVARIANT: There are no `fdget_pos` calls on the current thread, and by the type |
| 253 | + // invariants, if there is a `fdget_pos` call on another thread, then it took the |
| 254 | + // `f_pos_lock` mutex. |
| 255 | + // |
| 256 | + // SAFETY: `File<MaybeFdgetPos>` and `File<NoFdgetPos>` have the same layout. |
| 257 | + unsafe { ARef::from_raw(ARef::into_raw(me).cast()) } |
| 258 | + } |
| 259 | +} |
| 260 | + |
| 261 | +/// File methods that exist under all sharing modes. |
| 262 | +impl<S: FileShareMode> File<S> { |
| 263 | + /// Creates a reference to a [`File`] from a valid pointer. |
| 264 | + /// |
| 265 | + /// # Safety |
| 266 | + /// |
| 267 | + /// * The caller must ensure that `ptr` points at a valid file and that the file's refcount is |
| 268 | + /// positive for the duration of 'a. |
| 269 | + /// * The caller must ensure that the requirements for using the chosen file sharing mode are |
| 270 | + /// upheld. |
| 271 | + pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File<S> { |
| 272 | + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the |
| 273 | + // duration of 'a. The cast is okay because `File` is `repr(transparent)`. |
| 274 | + // |
| 275 | + // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during |
| 276 | + // 'a. The caller guarantees to uphold the requirements for the chosen sharing mode. |
| 277 | + unsafe { &*ptr.cast() } |
| 278 | + } |
| 279 | + |
| 280 | + /// Returns a raw pointer to the inner C struct. |
| 281 | + #[inline] |
| 282 | + pub fn as_ptr(&self) -> *mut bindings::file { |
| 283 | + self.inner.get() |
| 284 | + } |
| 285 | + |
| 286 | + /// Returns the flags associated with the file. |
| 287 | + /// |
| 288 | + /// The flags are a combination of the constants in [`flags`]. |
| 289 | + pub fn flags(&self) -> u32 { |
| 290 | + // This `read_volatile` is intended to correspond to a READ_ONCE call. |
| 291 | + // |
| 292 | + // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount. |
| 293 | + // |
| 294 | + // FIXME(read_once): Replace with `read_once` when available on the Rust side. |
| 295 | + unsafe { core::ptr::addr_of!((*self.as_ptr()).f_flags).read_volatile() } |
| 296 | + } |
| 297 | +} |
| 298 | + |
| 299 | +// SAFETY: The type invariants guarantee that `File` is always ref-counted. This implementation |
| 300 | +// makes `ARef<File>` own a normal refcount. |
| 301 | +unsafe impl<S: FileShareMode> AlwaysRefCounted for File<S> { |
| 302 | + fn inc_ref(&self) { |
| 303 | + // SAFETY: The existence of a shared reference means that the refcount is nonzero. |
| 304 | + unsafe { bindings::get_file(self.as_ptr()) }; |
| 305 | + } |
| 306 | + |
| 307 | + unsafe fn dec_ref(obj: ptr::NonNull<File<S>>) { |
| 308 | + // SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we |
| 309 | + // may drop it. The cast is okay since `File` has the same representation as `struct file`. |
| 310 | + unsafe { bindings::fput(obj.cast().as_ptr()) } |
| 311 | + } |
| 312 | +} |
| 313 | + |
| 314 | +/// Represents the `EBADF` error code. |
| 315 | +/// |
| 316 | +/// Used for methods that can only fail with `EBADF`. |
| 317 | +#[derive(Copy, Clone, Eq, PartialEq)] |
| 318 | +pub struct BadFdError; |
| 319 | + |
| 320 | +impl From<BadFdError> for Error { |
| 321 | + fn from(_: BadFdError) -> Error { |
| 322 | + EBADF |
| 323 | + } |
| 324 | +} |
| 325 | + |
| 326 | +impl core::fmt::Debug for BadFdError { |
| 327 | + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 328 | + f.pad("EBADF") |
| 329 | + } |
| 330 | +} |
0 commit comments