Skip to content

Commit 6b050e2

Browse files
committed
rust: fs: introduce FileSystem::lookup
This allows inodes to be created for directory entries. Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent 5c9b705 commit 6b050e2

3 files changed

Lines changed: 90 additions & 5 deletions

File tree

rust/kernel/error.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ impl Error {
131131
}
132132

133133
/// Returns the error encoded as a pointer.
134-
#[allow(dead_code)]
135134
pub(crate) fn to_ptr<T>(self) -> *mut T {
136135
// SAFETY: self.0 is a valid error due to its invariant.
137136
unsafe { bindings::ERR_PTR(self.0.into()) as *mut _ }

rust/kernel/fs.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ pub trait FileSystem {
2727
///
2828
/// [`DirEmitter::pos`] holds the current position of the directory reader.
2929
fn read_dir(inode: &INode<Self>, emitter: &mut DirEmitter) -> Result;
30+
31+
/// Returns the inode corresponding to the directory entry with the given name.
32+
fn lookup(parent: &INode<Self>, name: &[u8]) -> Result<ARef<INode<Self>>>;
3033
}
3134

3235
/// The types of directory entries reported by [`FileSystem::read_dir`].
@@ -220,8 +223,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
220223
let mode = match params.typ {
221224
INodeType::Dir => {
222225
inode.__bindgen_anon_3.i_fop = &Tables::<T>::DIR_FILE_OPERATIONS;
223-
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
224-
inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
226+
inode.i_op = &Tables::<T>::DIR_INODE_OPERATIONS;
225227
bindings::S_IFDIR
226228
}
227229
};
@@ -552,6 +554,62 @@ impl<T: FileSystem + ?Sized> Tables<T> {
552554
}
553555
})
554556
}
557+
558+
const DIR_INODE_OPERATIONS: bindings::inode_operations = bindings::inode_operations {
559+
lookup: Some(Self::lookup_callback),
560+
get_link: None,
561+
permission: None,
562+
get_inode_acl: None,
563+
readlink: None,
564+
create: None,
565+
link: None,
566+
unlink: None,
567+
symlink: None,
568+
mkdir: None,
569+
rmdir: None,
570+
mknod: None,
571+
rename: None,
572+
setattr: None,
573+
getattr: None,
574+
listxattr: None,
575+
fiemap: None,
576+
update_time: None,
577+
atomic_open: None,
578+
tmpfile: None,
579+
get_acl: None,
580+
set_acl: None,
581+
fileattr_set: None,
582+
fileattr_get: None,
583+
get_offset_ctx: None,
584+
};
585+
586+
extern "C" fn lookup_callback(
587+
parent_ptr: *mut bindings::inode,
588+
dentry: *mut bindings::dentry,
589+
_flags: u32,
590+
) -> *mut bindings::dentry {
591+
// SAFETY: The C API guarantees that `parent_ptr` is a valid inode.
592+
let parent = unsafe { &*parent_ptr.cast::<INode<T>>() };
593+
594+
// SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is
595+
// immutable, it's ok to read its length directly.
596+
let len = unsafe { (*dentry).d_name.__bindgen_anon_1.__bindgen_anon_1.len };
597+
let Ok(name_len) = usize::try_from(len) else {
598+
return ENOENT.to_ptr();
599+
};
600+
601+
// SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is
602+
// immutable, it's ok to read it directly.
603+
let name = unsafe { core::slice::from_raw_parts((*dentry).d_name.name, name_len) };
604+
match T::lookup(parent, name) {
605+
Err(e) => e.to_ptr(),
606+
// SAFETY: The returned inode is valid and referenced (by the type invariants), so
607+
// it is ok to transfer this increment to `d_splice_alias`.
608+
Ok(inode) => unsafe {
609+
bindings::d_splice_alias(ManuallyDrop::new(inode).0.get(), dentry)
610+
},
611+
}
612+
}
555613
}
556614

557615
/// Directory entry emitter.
@@ -637,7 +695,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
637695
/// # mod module_ro_fs_sample {
638696
/// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock};
639697
/// use kernel::prelude::*;
640-
/// use kernel::{c_str, fs};
698+
/// use kernel::{c_str, fs, types::ARef};
641699
///
642700
/// kernel::module_fs! {
643701
/// type: MyFs,
@@ -656,6 +714,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
656714
/// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result {
657715
/// todo!()
658716
/// }
717+
/// fn lookup(_: &INode<Self>, _: &[u8]) -> Result<ARef<INode<Self>>> {
718+
/// todo!()
719+
/// }
659720
/// }
660721
/// # }
661722
/// ```

samples/rust/rust_rofs.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use kernel::fs::{
66
DirEmitter, INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams,
77
};
88
use kernel::prelude::*;
9-
use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either};
9+
use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either};
1010

1111
kernel::module_fs! {
1212
type: RoFs,
@@ -89,4 +89,29 @@ impl fs::FileSystem for RoFs {
8989

9090
Ok(())
9191
}
92+
93+
fn lookup(parent: &INode<Self>, name: &[u8]) -> Result<ARef<INode<Self>>> {
94+
if parent.ino() != 1 {
95+
return Err(ENOENT);
96+
}
97+
98+
match name {
99+
b"subdir" => match parent.super_block().get_or_create_inode(2)? {
100+
Either::Left(existing) => Ok(existing),
101+
Either::Right(new) => new.init(INodeParams {
102+
typ: INodeType::Dir,
103+
mode: 0o555,
104+
size: 0,
105+
blocks: 1,
106+
nlink: 2,
107+
uid: 0,
108+
gid: 0,
109+
atime: UNIX_EPOCH,
110+
ctime: UNIX_EPOCH,
111+
mtime: UNIX_EPOCH,
112+
}),
113+
},
114+
_ => Err(ENOENT),
115+
}
116+
}
92117
}

0 commit comments

Comments
 (0)