Skip to content

Commit 46e0683

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 90288c9 commit 46e0683

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`].
@@ -214,8 +217,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
214217
let mode = match params.typ {
215218
INodeType::Dir => {
216219
inode.__bindgen_anon_3.i_fop = &Tables::<T>::DIR_FILE_OPERATIONS;
217-
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
218-
inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
220+
inode.i_op = &Tables::<T>::DIR_INODE_OPERATIONS;
219221
bindings::S_IFDIR
220222
}
221223
};
@@ -546,6 +548,62 @@ impl<T: FileSystem + ?Sized> Tables<T> {
546548
}
547549
})
548550
}
551+
552+
const DIR_INODE_OPERATIONS: bindings::inode_operations = bindings::inode_operations {
553+
lookup: Some(Self::lookup_callback),
554+
get_link: None,
555+
permission: None,
556+
get_inode_acl: None,
557+
readlink: None,
558+
create: None,
559+
link: None,
560+
unlink: None,
561+
symlink: None,
562+
mkdir: None,
563+
rmdir: None,
564+
mknod: None,
565+
rename: None,
566+
setattr: None,
567+
getattr: None,
568+
listxattr: None,
569+
fiemap: None,
570+
update_time: None,
571+
atomic_open: None,
572+
tmpfile: None,
573+
get_acl: None,
574+
set_acl: None,
575+
fileattr_set: None,
576+
fileattr_get: None,
577+
get_offset_ctx: None,
578+
};
579+
580+
extern "C" fn lookup_callback(
581+
parent_ptr: *mut bindings::inode,
582+
dentry: *mut bindings::dentry,
583+
_flags: u32,
584+
) -> *mut bindings::dentry {
585+
// SAFETY: The C API guarantees that `parent_ptr` is a valid inode.
586+
let parent = unsafe { &*parent_ptr.cast::<INode<T>>() };
587+
588+
// SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is
589+
// immutable, it's ok to read its length directly.
590+
let len = unsafe { (*dentry).d_name.__bindgen_anon_1.__bindgen_anon_1.len };
591+
let Ok(name_len) = usize::try_from(len) else {
592+
return ENOENT.to_ptr();
593+
};
594+
595+
// SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is
596+
// immutable, it's ok to read it directly.
597+
let name = unsafe { core::slice::from_raw_parts((*dentry).d_name.name, name_len) };
598+
match T::lookup(parent, name) {
599+
Err(e) => e.to_ptr(),
600+
// SAFETY: The returned inode is valid and referenced (by the type invariants), so
601+
// it is ok to transfer this increment to `d_splice_alias`.
602+
Ok(inode) => unsafe {
603+
bindings::d_splice_alias(ManuallyDrop::new(inode).0.get(), dentry)
604+
},
605+
}
606+
}
549607
}
550608

551609
/// Directory entry emitter.
@@ -620,7 +678,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
620678
/// # mod module_ro_fs_sample {
621679
/// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock};
622680
/// use kernel::prelude::*;
623-
/// use kernel::{c_str, fs};
681+
/// use kernel::{c_str, fs, types::ARef};
624682
///
625683
/// kernel::module_fs! {
626684
/// type: MyFs,
@@ -639,6 +697,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
639697
/// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result {
640698
/// todo!()
641699
/// }
700+
/// fn lookup(_: &INode<Self>, _: &[u8]) -> Result<ARef<INode<Self>>> {
701+
/// todo!()
702+
/// }
642703
/// }
643704
/// # }
644705
/// ```

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)