Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions kernel/crates/another_ext4/src/ext4/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,42 @@ impl Ext4 {
Ok(inode_ref)
}

/// Create a device inode (character or block device).
///
/// Unlike `create_inode()`, this function:
/// - Does NOT initialize the extent tree
/// - Stores the device number in i_block[0..1] (Linux ext4 standard)
#[inline(never)]
pub(super) fn create_device_inode(
&self,
mode: InodeMode,
major: u32,
minor: u32,
) -> Result<InodeRef> {
// Device nodes are never directories
let id = self.alloc_inode(false)?;

// Initialize the inode
let mut inode = Box::new(Inode::default());
inode.set_mode(mode);

// Key difference: set device number instead of extent tree
inode.set_device(major, minor);

let mut inode_ref = InodeRef::new(id, inode);

// Sync the inode to disk
self.write_inode_with_csum(&mut inode_ref);

trace!(
"Alloc device inode {} ({}:{}) ok",
inode_ref.id,
major,
minor
);
Ok(inode_ref)
}

/// Create(initialize) the root inode of the file system
#[inline(never)]
pub(super) fn create_root_inode(&self) -> Result<InodeRef> {
Expand Down
87 changes: 86 additions & 1 deletion kernel/crates/another_ext4/src/ext4/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ impl Ext4 {
if inode.inode.link_count() == 0 {
return_error!(ErrCode::EINVAL, "Invalid inode {}", id);
}

// Get device number for device nodes
let rdev = if inode.inode.is_device() {
inode.inode.device()
} else {
(0, 0)
};

Ok(FileAttr {
ino: id,
size: inode.inode.size(),
Expand All @@ -71,6 +79,7 @@ impl Ext4 {
links: inode.inode.link_count(),
uid: inode.inode.uid(),
gid: inode.inode.gid(),
rdev,
})
}

Expand Down Expand Up @@ -122,6 +131,31 @@ impl Ext4 {
Ok(())
}

/// Link a newly created inode into `parent`.
///
/// If linking fails, this function frees the newly allocated inode to avoid leaks.
fn link_new_inode_or_free(
&self,
parent: &mut InodeRef,
child: &mut InodeRef,
name: &str,
) -> Result<()> {
if let Err(link_err) = self.link_inode(parent, child, name) {
if let Err(cleanup_err) = self.free_inode(child) {
trace!(
"link failed for new inode {} (name {}), cleanup failed: {:?}; original link error: {:?}",
child.id,
name,
cleanup_err,
link_err
);
return Err(cleanup_err);
}
return Err(link_err);
}
Ok(())
}

/// Create a file. This function will not check the existence of
/// the file, call `lookup` to check beforehand.
///
Expand All @@ -148,11 +182,62 @@ impl Ext4 {
}
// Create child inode and link it to parent directory
let mut child = self.create_inode(mode)?;
self.link_inode(&mut parent, &mut child, name)?;
self.link_new_inode_or_free(&mut parent, &mut child, name)?;
// Create file handler
Ok(child.id)
}

/// Create a device node (character or block device).
///
/// Unlike `create()`, this function:
/// - Does NOT initialize the extent tree
/// - Stores the device number in i_block[0..1] (Linux ext4 standard)
///
/// # Params
///
/// * `parent` - parent directory inode id
/// * `name` - device node name
/// * `mode` - file type (must include CHARDEV or BLOCKDEV) and permissions
/// * `major` - major device number
/// * `minor` - minor device number
///
/// # Return
///
/// `Ok(inode)` - Inode id of the new device node
///
/// # Error
///
/// * `ENOTDIR` - `parent` is not a directory
/// * `ENOSPC` - No space left on device
pub fn mknod(
&self,
parent: InodeId,
name: &str,
mode: InodeMode,
major: u32,
minor: u32,
) -> Result<InodeId> {
let mut parent_ref = self.read_inode(parent);

// Can only create in a directory
if !parent_ref.inode.is_dir() {
return_error!(
ErrCode::ENOTDIR,
"Inode {} is not a directory",
parent_ref.id
);
}

// Create device inode (uses create_device_inode which sets device number)
let mut child = self.create_device_inode(mode, major, minor)?;

// Link to parent directory
self.link_new_inode_or_free(&mut parent_ref, &mut child, name)?;

trace!("mknod {} ({}:{}) -> inode {}", name, major, minor, child.id);
Ok(child.id)
}

/// Read data from a file. This function will read exactly `buf.len()`
/// bytes unless the end of the file is reached.
///
Expand Down
Loading
Loading