|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Files and file descriptors. |
| 4 | +//! |
| 5 | +//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and |
| 6 | +//! [`include/linux/file.h`](../../../../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 | + /// Also known as `O_NDELAY`. |
| 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 | +/// # Invariants |
| 101 | +/// |
| 102 | +/// Instances of this type are always ref-counted, that is, a call to `get_file` ensures that the |
| 103 | +/// allocation remains valid at least until the matching call to `fput`. |
| 104 | +#[repr(transparent)] |
| 105 | +pub struct File(Opaque<bindings::file>); |
| 106 | + |
| 107 | +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. |
| 108 | +// This means that the only situation in which a `File` can be accessed mutably is when the |
| 109 | +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so |
| 110 | +// it is ok for this type to be `Send`. |
| 111 | +unsafe impl Send for File {} |
| 112 | + |
| 113 | +// SAFETY: All methods defined on `File` that take `&self` are safe to call even if other threads |
| 114 | +// are concurrently accessing the same `struct file`, because those methods either access immutable |
| 115 | +// properties or have proper synchronization to ensure that such accesses are safe. |
| 116 | +unsafe impl Sync for File {} |
| 117 | + |
| 118 | +impl File { |
| 119 | + /// Constructs a new `struct file` wrapper from a file descriptor. |
| 120 | + /// |
| 121 | + /// The file descriptor belongs to the current process. |
| 122 | + pub fn fget(fd: u32) -> Result<ARef<Self>, BadFdError> { |
| 123 | + // SAFETY: FFI call, there are no requirements on `fd`. |
| 124 | + let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(BadFdError)?; |
| 125 | + |
| 126 | + // SAFETY: `fget` either returns null or a valid pointer to a file, and we checked for null |
| 127 | + // above. |
| 128 | + // |
| 129 | + // INVARIANT: `fget` increments the refcount before returning. |
| 130 | + Ok(unsafe { ARef::from_raw(ptr.cast()) }) |
| 131 | + } |
| 132 | + |
| 133 | + /// Creates a reference to a [`File`] from a valid pointer. |
| 134 | + /// |
| 135 | + /// # Safety |
| 136 | + /// |
| 137 | + /// The caller must ensure that `ptr` points at a valid file and that its refcount does not |
| 138 | + /// reach zero during the lifetime 'a. |
| 139 | + pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { |
| 140 | + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the |
| 141 | + // duration of 'a. The cast is okay because `File` is `repr(transparent)`. |
| 142 | + // |
| 143 | + // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during |
| 144 | + // 'a. |
| 145 | + unsafe { &*ptr.cast() } |
| 146 | + } |
| 147 | + |
| 148 | + /// Returns a raw pointer to the inner C struct. |
| 149 | + #[inline] |
| 150 | + pub fn as_ptr(&self) -> *mut bindings::file { |
| 151 | + self.0.get() |
| 152 | + } |
| 153 | + |
| 154 | + /// Returns the flags associated with the file. |
| 155 | + /// |
| 156 | + /// The flags are a combination of the constants in [`flags`]. |
| 157 | + pub fn flags(&self) -> u32 { |
| 158 | + // This `read_volatile` is intended to correspond to a READ_ONCE call. |
| 159 | + // |
| 160 | + // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount. |
| 161 | + // |
| 162 | + // TODO: Replace with `read_once` when available on the Rust side. |
| 163 | + unsafe { core::ptr::addr_of!((*self.as_ptr()).f_flags).read_volatile() } |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +// SAFETY: The type invariants guarantee that `File` is always ref-counted. |
| 168 | +unsafe impl AlwaysRefCounted for File { |
| 169 | + fn inc_ref(&self) { |
| 170 | + // SAFETY: The existence of a shared reference means that the refcount is nonzero. |
| 171 | + unsafe { bindings::get_file(self.as_ptr()) }; |
| 172 | + } |
| 173 | + |
| 174 | + unsafe fn dec_ref(obj: ptr::NonNull<Self>) { |
| 175 | + // SAFETY: The safety requirements guarantee that the refcount is nonzero. |
| 176 | + unsafe { bindings::fput(obj.cast().as_ptr()) } |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +/// Represents the `EBADF` error code. |
| 181 | +/// |
| 182 | +/// Used for methods that can only fail with `EBADF`. |
| 183 | +#[derive(Copy, Clone, Eq, PartialEq)] |
| 184 | +pub struct BadFdError; |
| 185 | + |
| 186 | +impl From<BadFdError> for Error { |
| 187 | + fn from(_: BadFdError) -> Error { |
| 188 | + EBADF |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +impl core::fmt::Debug for BadFdError { |
| 193 | + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 194 | + f.pad("EBADF") |
| 195 | + } |
| 196 | +} |
0 commit comments