Skip to content

Commit d2489f3

Browse files
committed
rust: fs: allow the icache lookup and creation of inodes
For now we only support directory inodes, but more will be added in subsequent commits. We now require the root inode to be specified when initialising a new super block (instead of crafting one ourselves). The `NewINode` type ensures that a new inode is properly initialised before it is marked so. It also facilitates error paths by automatically marking inodes as failed if they're not properly initialised. Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent 5e273b4 commit d2489f3

3 files changed

Lines changed: 180 additions & 35 deletions

File tree

rust/helpers.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ struct kunit *rust_helper_kunit_get_current_test(void)
145145
}
146146
EXPORT_SYMBOL_GPL(rust_helper_kunit_get_current_test);
147147

148+
void rust_helper_i_uid_write(struct inode *inode, uid_t uid)
149+
{
150+
i_uid_write(inode, uid);
151+
}
152+
EXPORT_SYMBOL_GPL(rust_helper_i_uid_write);
153+
154+
void rust_helper_i_gid_write(struct inode *inode, gid_t gid)
155+
{
156+
i_gid_write(inode, gid);
157+
}
158+
EXPORT_SYMBOL_GPL(rust_helper_i_gid_write);
159+
148160
off_t rust_helper_i_size_read(const struct inode *inode)
149161
{
150162
return i_size_read(inode);

rust/kernel/fs.rs

Lines changed: 142 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
1212
/// Read-only file systems.
1313
pub mod ro {
1414
use crate::error::{code::*, from_result, to_result, Error, Result};
15-
use crate::types::{AlwaysRefCounted, Opaque};
16-
use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule};
17-
use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin, ptr};
15+
use crate::types::{ARef, AlwaysRefCounted, Either, Opaque};
16+
use crate::{bindings, init::PinInit, str::CStr, time::Time, try_pin_init, ThisModule};
17+
use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
1818
use macros::{pin_data, pinned_drop};
1919

2020
/// A read-only file system type.
@@ -137,12 +137,138 @@ pub mod ro {
137137
}
138138
}
139139

140+
/// An inode that is locked and hasn't been initialised yet.
141+
#[repr(transparent)]
142+
pub struct NewINode<T: Type + ?Sized>(ARef<INode<T>>);
143+
144+
impl<T: Type + ?Sized> NewINode<T> {
145+
/// Initialises the new inode with the given parameters.
146+
pub fn init(self, params: INodeParams) -> Result<ARef<INode<T>>> {
147+
// SAFETY: This is a new inode, so it's safe to manipulate it mutably.
148+
let inode = unsafe { &mut *self.0 .0.get() };
149+
150+
let mode = match params.typ {
151+
INodeType::Dir => {
152+
// SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
153+
inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
154+
155+
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
156+
inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
157+
bindings::S_IFDIR
158+
}
159+
};
160+
161+
inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?;
162+
inode.i_size = params.size;
163+
inode.i_blocks = params.blocks;
164+
165+
inode.__i_ctime = params.ctime.into();
166+
inode.i_mtime = params.mtime.into();
167+
inode.i_atime = params.atime.into();
168+
169+
// SAFETY: inode is a new inode, so it is valid for write.
170+
unsafe {
171+
bindings::set_nlink(inode, params.nlink);
172+
bindings::i_uid_write(inode, params.uid);
173+
bindings::i_gid_write(inode, params.gid);
174+
bindings::unlock_new_inode(inode);
175+
}
176+
177+
// SAFETY: We are manually destructuring `self` and preventing `drop` from being
178+
// called.
179+
Ok(unsafe { (&ManuallyDrop::new(self).0 as *const ARef<INode<T>>).read() })
180+
}
181+
}
182+
183+
impl<T: Type + ?Sized> Drop for NewINode<T> {
184+
fn drop(&mut self) {
185+
// SAFETY: The new inode failed to be turned into an initialised inode, so it's safe
186+
// (and in fact required) to call `iget_failed` on it.
187+
unsafe { bindings::iget_failed(self.0 .0.get()) };
188+
}
189+
}
190+
191+
/// The type of the inode.
192+
#[derive(Copy, Clone)]
193+
pub enum INodeType {
194+
/// Directory type.
195+
Dir,
196+
}
197+
198+
/// Required inode parameters.
199+
///
200+
/// This is used when creating new inodes.
201+
pub struct INodeParams {
202+
/// The access mode. It's a mask that grants execute (1), write (2) and read (4) access to
203+
/// everyone, the owner group, and the owner.
204+
pub mode: u16,
205+
206+
/// Type of inode.
207+
///
208+
/// Also carries additional per-type data.
209+
pub typ: INodeType,
210+
211+
/// Size of the contents of the inode.
212+
///
213+
/// Its maximum value is [`super::MAX_LFS_FILESIZE`].
214+
pub size: i64,
215+
216+
/// Number of blocks.
217+
pub blocks: u64,
218+
219+
/// Number of links to the inode.
220+
pub nlink: u32,
221+
222+
/// User id.
223+
pub uid: u32,
224+
225+
/// Group id.
226+
pub gid: u32,
227+
228+
/// Creation time.
229+
pub ctime: Time,
230+
231+
/// Last modification time.
232+
pub mtime: Time,
233+
234+
/// Last access time.
235+
pub atime: Time,
236+
}
237+
140238
/// A file system super block.
141239
///
142240
/// Wraps the kernel's `struct super_block`.
143241
#[repr(transparent)]
144242
pub struct SuperBlock<T: Type + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
145243

244+
impl<T: Type + ?Sized> SuperBlock<T> {
245+
/// Tries to get an existing inode or create a new one if it doesn't exist yet.
246+
pub fn get_or_create_inode(&self, ino: u64) -> Result<Either<ARef<INode<T>>, NewINode<T>>> {
247+
// SAFETY: The only initialisation missing from the superblock is the root, and this
248+
// function is needed to create the root, so it's safe to call it.
249+
let inode = ptr::NonNull::new(unsafe { bindings::iget_locked(self.0.get(), ino) })
250+
.ok_or(ENOMEM)?;
251+
252+
// SAFETY: `inode` is valid for read, but there could be concurrent writers (e.g., if
253+
// it's an already-initialised inode), so we use `read_volatile` to read its current
254+
// state.
255+
let state = unsafe { ptr::read_volatile(ptr::addr_of!((*inode.as_ptr()).i_state)) };
256+
if state & u64::from(bindings::I_NEW) == 0 {
257+
// The inode is cached. Just return it.
258+
//
259+
// SAFETY: `inode` had its refcount incremented by `iget_locked`; this increment is
260+
// now owned by `ARef`.
261+
Ok(Either::Left(unsafe { ARef::from_raw(inode.cast()) }))
262+
} else {
263+
// SAFETY: The new inode is valid but not fully initialised yet, so it's ok to
264+
// create a `NewINode`.
265+
Ok(Either::Right(NewINode(unsafe {
266+
ARef::from_raw(inode.cast())
267+
})))
268+
}
269+
}
270+
}
271+
146272
/// Required superblock parameters.
147273
///
148274
/// This is used in [`NewSuperBlock::init`].
@@ -191,7 +317,7 @@ pub mod ro {
191317
}
192318

193319
/// Initialises the superblock.
194-
pub fn init(self, params: &SuperParams) -> Result<&'a SuperBlock<T>> {
320+
pub fn init(self, params: &SuperParams, root: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
195321
// SAFETY: Since this is a new super block, we hold the only reference to it.
196322
let sb = unsafe { &mut *self.sb.0.get() };
197323

@@ -207,35 +333,16 @@ pub mod ro {
207333
sb.s_blocksize = 1 << sb.s_blocksize_bits;
208334
sb.s_flags |= bindings::SB_RDONLY;
209335

210-
// The following is scaffolding code that will be removed in a subsequent patch. It is
211-
// needed to build a root dentry, otherwise core code will BUG().
212-
let inode = unsafe { bindings::new_inode(sb) };
213-
if inode.is_null() {
214-
return Err(ENOMEM);
215-
}
216-
217-
// SAFETY: `inode` is valid for write.
218-
unsafe { bindings::set_nlink(inode, 2) };
219-
220-
{
221-
// SAFETY: This is a newly-created inode. No other references to it exist, so it is
222-
// safe to mutably dereference it.
223-
let inode = unsafe { &mut *inode };
224-
inode.i_ino = 1;
225-
inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
226-
227-
// SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
228-
inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
229-
230-
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
231-
inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
336+
// Reject root inode if it belongs to a different superblock.
337+
if !ptr::eq(root.super_block(), self.sb) {
338+
return Err(EINVAL);
232339
}
233340

234341
// SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
235342
// case for this call.
236343
//
237344
// It takes over the inode, even on failure, so we don't need to clean it up.
238-
let dentry = unsafe { bindings::d_make_root(inode) };
345+
let dentry = unsafe { bindings::d_make_root(ManuallyDrop::new(root).0.get()) };
239346
if dentry.is_null() {
240347
return Err(ENOMEM);
241348
}
@@ -246,6 +353,14 @@ pub mod ro {
246353
}
247354
}
248355

356+
impl<T: Type + ?Sized> core::ops::Deref for NewSuperBlock<'_, T> {
357+
type Target = SuperBlock<T>;
358+
359+
fn deref(&self) -> &Self::Target {
360+
self.sb
361+
}
362+
}
363+
249364
struct Tables<T: Type + ?Sized>(T);
250365
impl<T: Type + ?Sized> Tables<T> {
251366
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {

samples/rust/rust_rofs.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
//! Rust read-only file system sample.
44
5-
use kernel::fs::ro::{NewSuperBlock, SuperBlock, SuperParams};
5+
use kernel::fs::ro::{INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams};
66
use kernel::prelude::*;
7-
use kernel::{c_str, fs};
7+
use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either};
88

99
kernel::module_ro_fs! {
1010
type: RoFs,
@@ -19,11 +19,29 @@ impl fs::ro::Type for RoFs {
1919
const NAME: &'static CStr = c_str!("rust-fs");
2020

2121
fn fill_super(sb: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
22-
sb.init(&SuperParams {
23-
magic: 0x52555354,
24-
blocksize_bits: 12,
25-
maxbytes: fs::MAX_LFS_FILESIZE,
26-
time_gran: 1,
27-
})
22+
let root = match sb.get_or_create_inode(1)? {
23+
Either::Left(existing) => existing,
24+
Either::Right(new) => new.init(INodeParams {
25+
typ: INodeType::Dir,
26+
mode: 0o555,
27+
size: 1,
28+
blocks: 1,
29+
nlink: 2,
30+
uid: 0,
31+
gid: 0,
32+
atime: UNIX_EPOCH,
33+
ctime: UNIX_EPOCH,
34+
mtime: UNIX_EPOCH,
35+
})?,
36+
};
37+
sb.init(
38+
&SuperParams {
39+
magic: 0x52555354,
40+
blocksize_bits: 12,
41+
maxbytes: fs::MAX_LFS_FILESIZE,
42+
time_gran: 1,
43+
},
44+
root,
45+
)
2846
}
2947
}

0 commit comments

Comments
 (0)