Skip to content

Commit

Permalink
Basic naming service now working, including a tmpfs; new syscals: Ope…
Browse files Browse the repository at this point in the history
…n, Read, Write, Seek, Close, Touch, Readdir, Cwd, Cd
  • Loading branch information
schoettner committed Jan 5, 2025
1 parent 2dd9d35 commit 4d01499
Show file tree
Hide file tree
Showing 18 changed files with 1,149 additions and 702 deletions.
2 changes: 2 additions & 0 deletions os/kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ path = "src/lib.rs"
graphic = { path = "../library/graphic" }
stream = { path = "../library/stream" }
syscall = { path = "../library/syscall" }
naming = { path = "../library/naming" }

# External depencies
spin = "0.9.8"
Expand All @@ -38,6 +39,7 @@ pci_types = "0.10.0"
bitflags = "2.6.0"
smoltcp = { version = "0.11.0", default-features = false, features = ["alloc", "log", "medium-ethernet", "proto-ipv4", "socket-udp"] }
mbrs = { version = "0.3.1", default-features = false, features = ["no-std"] }
num_enum = { version = "0.7", default-features = false }

[build-dependencies]
built = { version = "0.7.5", features = ["chrono", "git2"] }
301 changes: 224 additions & 77 deletions os/kernel/src/naming/api.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,54 @@
/* ╔═════════════════════════════════════════════════════════════════════════╗
║ Module: api ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Descr.: Public interface of the name service (ns):
║ - mkdir: create a directory with all sub directories
║ Descr.: Public interface of the naming service:
║ - init: init ns, called once
║ - open: open a named object ║
║ - read: read bytes from an open object ║
║ - write: write bytes into an open object ║
║ - seek: set file pointer (for files) ║
║ - init: init ns, called once
║ - dump: print all entries on the screen (for debugging)
║ - mkdir: create a directory
║ - touch: create a file
╟─────────────────────────────────────────────────────────────────────────╢
║ Author: Michael Schoettner, Univ. Duesseldorf, 9.9.2024
║ Author: Michael Schoettner, Univ. Duesseldorf, 30.12.2024 ║
╚═════════════════════════════════════════════════════════════════════════╝
*/

use alloc::string::{String, ToString};
use alloc::sync::Arc;
use alloc::vec::Vec;
use log::info;
use spin::{Mutex, Once};

use alloc::string::String;
use syscall::return_vals::{OpenOptions, SeekOrigin, convert_syscall_result_to_ret_code};
use super::traits::FileSystem;
use super::lookup;
use super::open_objects;
use super::stat::Mode;
use super::tmpfs;

use crate::naming::main;
use crate::naming::main::ns_get;
use crate::naming::open_objects::ns_get_oot;
use naming::shared_types::{OpenOptions, RawDirent, SeekOrigin};
use syscall::return_vals::Errno;

// root of naming service
pub(super) static ROOT: Once<Arc<dyn FileSystem>> = Once::new();

///
/// Description:
/// Init function of NS. Must be called once before using it.
///
pub fn init() {
main::init();
}
// current working directory
static CWD: Mutex<String> = Mutex::new(String::new());

///
/// Description: Create a directory (including sub directories) for the given path
/// Description: Initilize the naming service (must be called once before using it)
///
/// Parameters: `path` The path
///
/// Return: `SyscallResult`
///
pub fn mkdir(path: &String) -> isize {
let result = ns_get().mkdir(path);
let sysret;
match result {
Ok(()) => sysret = Ok(0), // Convert the success case to a meaningful u64, e.g., 0
Err(e) => sysret = Err(e), // Forward the error directly
}
return convert_syscall_result_to_ret_code(sysret);
pub fn init() {
// Initialize ROOT with TmpFs
ROOT.call_once(|| {
let tmpfs = tmpfs::TmpFs::new();
Arc::new(tmpfs)
});
open_objects::open_object_table_init();
let mut cwd = CWD.lock();
*cwd = "/".to_string();
info!("naming service initialized");
// test::running_tests();
}

///
Expand All @@ -57,24 +60,15 @@ pub fn mkdir(path: &String) -> isize {
///
/// Return: `SyscallResult`
///
pub fn open(path: &String, flags: OpenOptions) -> isize {
let res = ns_get().open(path, flags);
let sysret;
match res {
Ok(fptr) => {
sysret = ns_get_oot().lock().create_new_handle_for_filepointer(fptr);
},
Err(e) => sysret = Err(e),
}
return convert_syscall_result_to_ret_code(sysret);

/* match res {
Ok(fptr) => ns_get_oot().lock().create_new_handle_for_filepointer(fptr),
Err(e) => Err(e),
}
*/
pub fn open(path: &String, flags: OpenOptions) -> Result<usize, Errno> {
open_objects::open(path, flags).or_else(|e| {
if flags.contains(OpenOptions::CREATE) {
touch(path).and_then(|_| open_objects::open(path, flags))
} else {
Err(e)
}
})
}

///
/// Description: \
/// Write bytes from the given buffer into the file (at the current position). \
Expand All @@ -86,13 +80,23 @@ pub fn open(path: &String, flags: OpenOptions) -> isize {
///
/// Return: `Ok(#bytes written)` or `Err(Errno)`
///
pub fn write(fh: usize, buf: &[u8]) -> isize {
let sysret;
match ns_get_oot().lock().get(fh) {
Ok(fptr) => sysret = fptr.write(buf),
Err(e) => sysret = Err(e),
}
return convert_syscall_result_to_ret_code(sysret);
pub fn write(fh: usize, buf: &[u8]) -> Result<usize, Errno> {
open_objects::write(fh, &buf)
}

///
/// Description: \
/// Read bytes from the file (from current position) into the given buffer. \
/// The number of bytes to be read is determined by the buffer size
///
/// Parameters: \
/// `fh` file handle \
/// `buf` buffer to copy file bytes into \
///
/// Return: `Ok(#bytes read)` or `Err(errno)`
///
pub fn read(fh: usize, buf: &mut [u8]) -> Result<usize, Errno> {
open_objects::read(fh, buf)
}

///
Expand All @@ -103,43 +107,186 @@ pub fn write(fh: usize, buf: &[u8]) -> isize {
/// `offset` offset in bytes \
/// `origin` point of origin
///
/// Return: `Ok(size in bytes)` or `Err(errno)`
/// Return: `Ok(0)` or `Err(errno)`
///
pub fn seek(fh: usize, offset: usize, origin: SeekOrigin) -> Result<usize, Errno> {
open_objects::seek(fh, offset, origin)
}

///
/// Description: Close an open object \
/// Return: `Ok(0)` or `Err(errno)`
///
pub fn seek(fh: usize, offset: usize, origin: SeekOrigin) -> isize {
let sysret;
match ns_get_oot().lock().get(fh) {
Ok(fptr) => sysret = fptr.seek(offset, origin),
Err(e) => sysret = Err(e),
pub fn close(fh: usize) -> Result<usize, Errno> {
open_objects::close(fh)
}

///
/// Description: Create a directory for the given path \
/// Parameters: `path` absolute path \
/// Return: `Ok(0)` or `Err(errno)`
///
pub fn mkdir(path: &String) -> Result<usize, Errno> {
// Split the path into components
let mut components: Vec<&str> = path.split("/").collect();

// Remove the last component (the name of the new directory)
let new_dir_name = components.pop();

// We need parent directory to create the new directory
let parent_dir;
if components.len() == 1 {
parent_dir = "/".to_string();
} else {
parent_dir = components.join("/"); // Joins the remaining components
}

// Safely lookup the parent directory and create the new file
let result = lookup::lookup_dir(&parent_dir)
.and_then(|dir| {
new_dir_name
.ok_or(Errno::EINVAL) // Handle missing file name
.and_then(|name| dir.create_dir(name, Mode::new(0))) // Create the file
})
.map(|_| 0); // Convert the success result to 0

match result {
Ok(_) => Ok(0), // Successfully created the file
Err(_) => {
// Handle the error here (e.g., logging or returning the error code)
Err(Errno::ENOTDIR)
}
}
}

///
/// Description: Create a new empty file \
/// Parameters: `path` absolute path \
/// Return: `0` if successful, otherwise an error code
///
pub fn touch(path: &String) -> Result<usize, Errno> {
// Split the path into components
let mut components: Vec<&str> = path.split("/").collect();

// Remove the last component (the name of the new file)
let new_file_name = components.pop();

// We need parent directory to create the new file
let parent_dir;
if components.len() == 1 {
parent_dir = "/".to_string();
} else {
parent_dir = components.join("/"); // Joins the remaining components
}

// Safely lookup the parent directory and create the new file
let result = lookup::lookup_dir(&parent_dir)
.and_then(|dir| {
new_file_name
.ok_or(Errno::EINVAL) // Handle missing file name
.and_then(|name| dir.create_file(name, Mode::new(0))) // Create the file
})
.map(|_| 0); // Convert the success result to 0

match result {
Ok(_) => Ok(0), // Successfully created the file
Err(_) => {
// Handle the error here (e.g., logging or returning the error code)
Err(Errno::ENOTDIR)
}
}
return convert_syscall_result_to_ret_code(sysret);
}

///
/// Description: \
/// Read bytes from the file (from current position) into the given buffer. \
/// The number of bytes to be read is determined by the buffer size
/// Read next directory entry
///
/// Parameters: \
/// `fh` file handle \
/// `buf` buffer to copy file bytes into \
/// `fh` handle to an open directory \
/// `dentry` memory for the next directory entry to be returned \
///
/// Return: `Ok(#bytes read)` or `Err(errno)`
/// Return: \
/// `Ok(1)` next directory entry in `dentry` \
/// `Ok(0)` no more entries in the directory \
/// `Err` error code
///
pub fn read(fh: usize, buf: &mut [u8]) -> isize {
let sysret;
match ns_get_oot().lock().get(fh) {
Ok(fptr) => sysret = fptr.read(buf),
Err(e) => sysret = Err(e),
pub fn readdir(fh: usize, dentry: *mut RawDirent) -> Result<usize, Errno> {
let res = open_objects::readdir(fh);
match res {
Ok(dir_entry) => {
match dir_entry {
Some(dir_entry_data) => {
// copy data
let mut de: RawDirent = RawDirent::new();
de.d_type = dir_entry_data.file_type as usize;
let name_bytes: &[u8] = dir_entry_data.name.as_bytes();
let len = name_bytes.len().min(255); // Avoid overflow
de.d_name[..len].copy_from_slice(&name_bytes[..len]);

// Write the Dirent structure to the provided dentry pointer
unsafe {
if !dentry.is_null() {
*dentry = de;
return Ok(1); // Indicate success
} else {
return Err(Errno::EUNKN); // Handle null pointer case
}
}
}
None => Ok(0),
}
}
Err(e) => Err(e),
}
return convert_syscall_result_to_ret_code(sysret);
}

///
/// Description: Dump all named objects on the screen (for debugging)
/// Description: \
/// Return the current working path.
///
/// Parameters: \
/// `buf` buffer to copy current path into \
///
/// Return: `Ok(#len of string)` or `Err(errno)`
///
pub fn cwd(buf: &mut [u8]) -> Result<usize, Errno> {
// Lock the CWD mutex to access its value
let cwd = CWD.lock();

// Get the string as bytes
let cwd_bytes = cwd.as_bytes();

// Calculate how much data can be copied (leave space for the null terminator)
let len_to_copy = (buf.len() - 1).min(cwd_bytes.len()); // Reserve space for the null terminator

// Copy the data into the buffer
buf[..len_to_copy].copy_from_slice(&cwd_bytes[..len_to_copy]);

// Add the null terminator if there is space
if buf.len() > len_to_copy {
buf[len_to_copy] = 0;
}

// Return the total length including the null terminator, or just the copied length
Ok(len_to_copy + 1)
}

///
/// Description: Change working directory \
/// Parameters: `path` absolute path \
/// Return: `Ok(0)` or `Err(errno)`
///
pub fn dump() -> i64 {
ns_get().dump();
return 0;
}
pub fn cd(path: &String) -> Result<usize, Errno> {
let result = lookup::lookup_dir(&path);
match result {
Ok(_) => {
let mut cwd = CWD.lock();
*cwd = path.clone();
Ok(0)
},
Err(_) => {
// Handle the error here (e.g., logging or returning the error code)
Err(Errno::ENOTDIR)
}
}
}
Loading

0 comments on commit 4d01499

Please sign in to comment.