Skip to content

Commit 739c1fb

Browse files
authored
Merge pull request #308 from wedsonaf/file-object
Automatically handle reference count on `File` instances.
2 parents 698569d + 39f305c commit 739c1fb

File tree

4 files changed

+69
-22
lines changed

4 files changed

+69
-22
lines changed

rust/kernel/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/miscdevice.h>
1414
#include <linux/poll.h>
1515
#include <linux/mm.h>
16+
#include <linux/file.h>
1617
#include <uapi/linux/android/binder.h>
1718
#include <linux/platform_device.h>
1819
#include <linux/of_platform.h>

rust/kernel/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ impl Error {
5353
/// Interrupted system call.
5454
pub const EINTR: Self = Error(-(bindings::EINTR as i32));
5555

56+
/// Bad file number.
57+
pub const EBADF: Self = Error(-(bindings::EBADF as i32));
58+
5659
/// Creates an [`Error`] from a kernel error code.
5760
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
5861
Error(errno)

rust/kernel/file.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,32 @@
55
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and
66
//! [`include/linux/file.h`](../../../../include/linux/file.h)
77
8-
use crate::bindings;
8+
use crate::{bindings, error::Error, Result};
9+
use core::{mem::ManuallyDrop, ops::Deref};
910

1011
/// Wraps the kernel's `struct file`.
1112
///
1213
/// # Invariants
1314
///
14-
/// The pointer [`File::ptr`] is non-null and valid.
15+
/// The pointer [`File::ptr`] is non-null and valid. Its reference count is also non-zero.
1516
pub struct File {
16-
pub(crate) ptr: *const bindings::file,
17+
pub(crate) ptr: *mut bindings::file,
1718
}
1819

1920
impl File {
20-
/// Constructs a new [`struct file`] wrapper.
21+
/// Constructs a new [`struct file`] wrapper from a file descriptor.
2122
///
22-
/// # Safety
23-
///
24-
/// The pointer `ptr` must be non-null and valid for the lifetime of the object.
25-
pub(crate) unsafe fn from_ptr(ptr: *const bindings::file) -> File {
26-
// INVARIANTS: the safety contract ensures the type invariant will hold.
27-
File { ptr }
23+
/// The file descriptor belongs to the current process.
24+
pub fn from_fd(fd: u32) -> Result<Self> {
25+
// SAFETY: FFI call, there are no requirements on `fd`.
26+
let ptr = unsafe { bindings::fget(fd) };
27+
if ptr.is_null() {
28+
return Err(Error::EBADF);
29+
}
30+
31+
// INVARIANTS: We checked that `ptr` is non-null, so it is valid. `fget` increments the ref
32+
// count before returning.
33+
Ok(Self { ptr })
2834
}
2935

3036
/// Returns the current seek/cursor/pointer position (`struct file::f_pos`).
@@ -39,3 +45,40 @@ impl File {
3945
unsafe { (*self.ptr).f_flags & bindings::O_NONBLOCK == 0 }
4046
}
4147
}
48+
49+
impl Drop for File {
50+
fn drop(&mut self) {
51+
// SAFETY: The type invariants guarantee that `File::ptr` has a non-zero reference count.
52+
unsafe { bindings::fput(self.ptr) };
53+
}
54+
}
55+
56+
/// A wrapper for [`File`] that doesn't automatically decrement that refcount when dropped.
57+
///
58+
/// We need the wrapper because [`ManuallyDrop`] alone would allow callers to call
59+
/// [`ManuallyDrop::into_inner`]. This would allow an unsafe sequence to be triggered without
60+
/// `unsafe` blocks because it would trigger an unbalanced call to `fput`.
61+
///
62+
/// # Invariants
63+
///
64+
/// The wrapped [`File`] remains valid for the lifetime of the object.
65+
pub(crate) struct FileRef(ManuallyDrop<File>);
66+
67+
impl FileRef {
68+
/// Constructs a new [`struct file`] wrapper that doesn't change its reference count.
69+
///
70+
/// # Safety
71+
///
72+
/// The pointer `ptr` must be non-null and valid for the lifetime of the object.
73+
pub(crate) unsafe fn from_ptr(ptr: *mut bindings::file) -> Self {
74+
Self(ManuallyDrop::new(File { ptr }))
75+
}
76+
}
77+
78+
impl Deref for FileRef {
79+
type Target = File;
80+
81+
fn deref(&self) -> &Self::Target {
82+
self.0.deref()
83+
}
84+
}

rust/kernel/file_operations.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use alloc::boxed::Box;
1212
use crate::{
1313
bindings, c_types,
1414
error::{Error, Result},
15-
file::File,
15+
file::{File, FileRef},
1616
from_kernel_result,
1717
io_buffer::{IoBufferReader, IoBufferWriter},
1818
iov_iter::IovIter,
@@ -102,7 +102,7 @@ unsafe extern "C" fn read_callback<T: FileOperations>(
102102
let f = &*((*file).private_data as *const T);
103103
// No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
104104
// See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
105-
let read = f.read(&File::from_ptr(file), &mut data, (*offset).try_into()?)?;
105+
let read = f.read(&FileRef::from_ptr(file), &mut data, (*offset).try_into()?)?;
106106
(*offset) += bindings::loff_t::try_from(read).unwrap();
107107
Ok(read as _)
108108
}
@@ -117,7 +117,7 @@ unsafe extern "C" fn read_iter_callback<T: FileOperations>(
117117
let file = (*iocb).ki_filp;
118118
let offset = (*iocb).ki_pos;
119119
let f = &*((*file).private_data as *const T);
120-
let read = f.read(&File::from_ptr(file), &mut iter, offset.try_into()?)?;
120+
let read = f.read(&FileRef::from_ptr(file), &mut iter, offset.try_into()?)?;
121121
(*iocb).ki_pos += bindings::loff_t::try_from(read).unwrap();
122122
Ok(read as _)
123123
}
@@ -134,7 +134,7 @@ unsafe extern "C" fn write_callback<T: FileOperations>(
134134
let f = &*((*file).private_data as *const T);
135135
// No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
136136
// See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
137-
let written = f.write(&File::from_ptr(file), &mut data, (*offset).try_into()?)?;
137+
let written = f.write(&FileRef::from_ptr(file), &mut data, (*offset).try_into()?)?;
138138
(*offset) += bindings::loff_t::try_from(written).unwrap();
139139
Ok(written as _)
140140
}
@@ -149,7 +149,7 @@ unsafe extern "C" fn write_iter_callback<T: FileOperations>(
149149
let file = (*iocb).ki_filp;
150150
let offset = (*iocb).ki_pos;
151151
let f = &*((*file).private_data as *const T);
152-
let written = f.write(&File::from_ptr(file), &mut iter, offset.try_into()?)?;
152+
let written = f.write(&FileRef::from_ptr(file), &mut iter, offset.try_into()?)?;
153153
(*iocb).ki_pos += bindings::loff_t::try_from(written).unwrap();
154154
Ok(written as _)
155155
}
@@ -160,7 +160,7 @@ unsafe extern "C" fn release_callback<T: FileOperations>(
160160
file: *mut bindings::file,
161161
) -> c_types::c_int {
162162
let ptr = mem::replace(&mut (*file).private_data, ptr::null_mut());
163-
T::release(T::Wrapper::from_pointer(ptr as _), &File::from_ptr(file));
163+
T::release(T::Wrapper::from_pointer(ptr as _), &FileRef::from_ptr(file));
164164
0
165165
}
166166

@@ -177,7 +177,7 @@ unsafe extern "C" fn llseek_callback<T: FileOperations>(
177177
_ => return Err(Error::EINVAL),
178178
};
179179
let f = &*((*file).private_data as *const T);
180-
let off = f.seek(&File::from_ptr(file), off)?;
180+
let off = f.seek(&FileRef::from_ptr(file), off)?;
181181
Ok(off as bindings::loff_t)
182182
}
183183
}
@@ -191,7 +191,7 @@ unsafe extern "C" fn unlocked_ioctl_callback<T: FileOperations>(
191191
let f = &*((*file).private_data as *const T);
192192
// SAFETY: This function is called by the kernel, so it must set `fs` appropriately.
193193
let mut cmd = IoctlCommand::new(cmd as _, arg as _);
194-
let ret = f.ioctl(&File::from_ptr(file), &mut cmd)?;
194+
let ret = f.ioctl(&FileRef::from_ptr(file), &mut cmd)?;
195195
Ok(ret as _)
196196
}
197197
}
@@ -205,7 +205,7 @@ unsafe extern "C" fn compat_ioctl_callback<T: FileOperations>(
205205
let f = &*((*file).private_data as *const T);
206206
// SAFETY: This function is called by the kernel, so it must set `fs` appropriately.
207207
let mut cmd = IoctlCommand::new(cmd as _, arg as _);
208-
let ret = f.compat_ioctl(&File::from_ptr(file), &mut cmd)?;
208+
let ret = f.compat_ioctl(&FileRef::from_ptr(file), &mut cmd)?;
209209
Ok(ret as _)
210210
}
211211
}
@@ -216,7 +216,7 @@ unsafe extern "C" fn mmap_callback<T: FileOperations>(
216216
) -> c_types::c_int {
217217
from_kernel_result! {
218218
let f = &*((*file).private_data as *const T);
219-
f.mmap(&File::from_ptr(file), &mut *vma)?;
219+
f.mmap(&FileRef::from_ptr(file), &mut *vma)?;
220220
Ok(0)
221221
}
222222
}
@@ -232,7 +232,7 @@ unsafe extern "C" fn fsync_callback<T: FileOperations>(
232232
let end = end.try_into()?;
233233
let datasync = datasync != 0;
234234
let f = &*((*file).private_data as *const T);
235-
let res = f.fsync(&File::from_ptr(file), start, end, datasync)?;
235+
let res = f.fsync(&FileRef::from_ptr(file), start, end, datasync)?;
236236
Ok(res.try_into().unwrap())
237237
}
238238
}
@@ -242,7 +242,7 @@ unsafe extern "C" fn poll_callback<T: FileOperations>(
242242
wait: *mut bindings::poll_table_struct,
243243
) -> bindings::__poll_t {
244244
let f = &*((*file).private_data as *const T);
245-
match f.poll(&File::from_ptr(file), &PollTable::from_ptr(wait)) {
245+
match f.poll(&FileRef::from_ptr(file), &PollTable::from_ptr(wait)) {
246246
Ok(v) => v,
247247
Err(_) => bindings::POLLERR,
248248
}

0 commit comments

Comments
 (0)