Skip to content
Draft
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
180 changes: 171 additions & 9 deletions src/rawposix/src/fs_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@ use parking_lot::RwLock;
use std::sync::atomic::{AtomicI32, AtomicU64};
use std::sync::Arc;
use typemap::type_conv::get_pipearray;
use typemap::fs_conv::*;

/// Helper function for close_syscall
///
/// This function will perform kernel close when necessary
///
/// Lind-WASM is running as same Linux-Process from host kernel perspective, so standard fds shouldn't
/// be closed in Lind-WASM execution, which preventing issues where other threads might reassign these
/// fds, causing unintended behavior or errors.
///
/// This function is registered in `fdtables` when creating the cage
pub fn kernel_close(fdentry: fdtables::FDTableEntry, _count: u64) {
let _ret = unsafe { libc::close(fdentry.underfd as i32) };
let kernel_fd = fdentry.underfd as i32;

if kernel_fd == STDIN_FILENO || kernel_fd == STDOUT_FILENO || kernel_fd == STDERR_FILENO {
return;
}

let ret = unsafe { libc::close(fdentry.underfd as i32) };
if ret < 0 {
let errno = get_errno();
panic!("kernel_close failed with errno: {:?}", errno);
}
}

/// Reference to Linux: https://man7.org/linux/man-pages/man2/open.2.html
Expand All @@ -17,14 +32,17 @@ pub fn kernel_close(fdentry: fdtables::FDTableEntry, _count: u64) {
/// after getting the kernel fd. `fdtables` currently only manage when a fd should be closed after open, so
/// then we need to set `O_CLOEXEC` flags according to input.
///
/// Input:
/// ## Arguments:
/// This call will only have one cageid indicates current cage, and three regular arguments same with Linux
/// - cageid: current cage
/// - path_arg: This argument points to a pathname naming the file. User's perspective.
/// - oflag_arg: This argument contains the file status flags and file access modes which will be alloted to
/// the open file description. The flags are combined together using a bitwise-inclusive-OR and the
/// result is passed as an argument to the function. We need to check if `O_CLOEXEC` has been set.
/// - mode_arg: This represents the permission of the newly created file. Directly passing to kernel.
///
/// ## Returns:
/// same with man page
pub fn open_syscall(
cageid: u64,
path_arg: u64,
Expand Down Expand Up @@ -54,7 +72,6 @@ pub fn open_syscall(
return syscall_error(Errno::EFAULT, "open_syscall", "Invalide Cage ID");
}

println!("[open_syscall] path: {:?}", path);
// Get the kernel fd first
let kernel_fd = unsafe { libc::open(path.as_ptr(), oflag, mode) };

Expand Down Expand Up @@ -84,7 +101,7 @@ pub fn open_syscall(
/// Since we implement a file descriptor management subsystem (called `fdtables`), we first translate the virtual file
/// descriptor into the corresponding kernel file descriptor before invoking the kernel's `libc::read()` function.
///
/// Input:
/// ## Arguments:
/// This call will have one cageid indicating the current cage, and several regular arguments similar to Linux:
/// - cageid: current cage identifier.
/// - virtual_fd: the virtual file descriptor from the RawPOSIX environment.
Expand Down Expand Up @@ -147,7 +164,7 @@ pub fn read_syscall(
/// subsystem (called `fdtables`) to handle virtual file descriptors. This syscall removes the virtual file
/// descriptor from the subsystem, and if necessary, closes the underlying kernel file descriptor.
///
/// Input:
/// ## Arguments:
/// This call will have one cageid indicating the current cage, and several regular arguments similar to Linux:
/// - cageid: current cage identifier.
/// - virtual_fd: the virtual file descriptor from the RawPOSIX environment to be closed.
Expand Down Expand Up @@ -193,12 +210,12 @@ pub fn close_syscall(
/// RawPOSIX doesn't have any other operations, so all operations will be handled by host. RawPOSIX does error handling
/// for this syscall.
///
/// Input:
/// ## Arguments:
/// - cageid: current cageid
/// - path_arg: This argument points to a pathname naming the file. User's perspective.
/// - mode_arg: This represents the permission of the newly created file. Directly passing to kernel.
///
/// Return:
/// ## Returns:
/// - return zero on success. On error, -1 is returned and errno is set to indicate the error.
pub fn mkdir_syscall(
cageid: u64,
Expand Down Expand Up @@ -237,3 +254,148 @@ pub fn mkdir_syscall(
}
ret
}

/// Reference to Linux: https://man7.org/linux/man-pages/man2/pipe.2.html
///
/// Linux `pipe()` syscall is equivalent to calling `pipe2()` with flags set to zero.
/// Therefore, our implementation simply delegates to pipe2_syscall with flags = 0.
///
/// Input:
/// - cageid: current cage identifier.
/// - pipefd_arg: a u64 representing the pointer to the PipeArray (user's perspective).
/// - pipefd_cageid: cage identifier for the pointer argument.
pub fn pipe_syscall(
cageid: u64,
pipefd_arg: u64,
pipefd_cageid: u64,
arg2: u64,
arg2_cageid: u64,
arg3: u64,
arg3_cageid: u64,
arg4: u64,
arg4_cageid: u64,
arg5: u64,
arg5_cageid: u64,
arg6: u64,
arg6_cageid: u64,
) -> i32 {
// Delegate to pipe2_syscall with flags set to 0.
pipe2_syscall(
cageid,
pipefd_arg,
pipefd_cageid,
0,
0,
arg3,
arg3_cageid,
arg4,
arg4_cageid,
arg5,
arg5_cageid,
arg6,
arg6_cageid,
)
}

/// Reference to Linux: https://man7.org/linux/man-pages/man2/pipe2.2.html
///
/// Linux `pipe2()` syscall creates a unidirectional data channel and returns two file descriptors,
/// one for reading and one for writing. In our implementation, we first convert the user-supplied
/// pointer to a mutable reference to a PipeArray. Then, we call libc::pipe2() with the provided flags.
/// Finally, we obtain new virtual file descriptors for both ends of the pipe using our fd management
/// subsystem (`fdtables`).
///
/// Input:
/// - cageid: current cage identifier.
/// - pipefd_arg: a u64 representing the pointer to the PipeArray (user's perspective).
/// - pipefd_cageid: cage identifier for the pointer argument.
/// - flags_arg: this argument contains flags (e.g., O_CLOEXEC) to be passed to pipe2.
/// - flags_cageid: cage identifier for the flags argument.
pub fn pipe2_syscall(
cageid: u64,
pipefd_arg: u64,
pipefd_cageid: u64,
flags_arg: u64,
flags_cageid: u64,
arg3: u64,
arg3_cageid: u64,
arg4: u64,
arg4_cageid: u64,
arg5: u64,
arg5_cageid: u64,
arg6: u64,
arg6_cageid: u64,
) -> i32 {
// Convert the flags argument.
let flags = sc_convert_sysarg_to_i32(flags_arg, flags_cageid, cageid);

// Validate flags - only O_NONBLOCK and O_CLOEXEC are allowed
let allowed_flags = fs_const::O_NONBLOCK | fs_const::O_CLOEXEC;
if flags & !allowed_flags != 0 {
return syscall_error(Errno::EINVAL, "pipe2_syscall", "Invalid flags");
}

// Ensure unused arguments are truly unused.
if !(sc_unusedarg(arg3, arg3_cageid)
&& sc_unusedarg(arg4, arg4_cageid)
&& sc_unusedarg(arg5, arg5_cageid)
&& sc_unusedarg(arg6, arg6_cageid))
{
return syscall_error(Errno::EFAULT, "pipe2_syscall", "Invalid Cage ID");
}
// Convert the u64 pointer into a mutable reference to PipeArray.
let pipefd = match get_pipearray(pipefd_arg) {
Ok(p) => p,
Err(e) => return e,
};
// Create an array to hold the two kernel file descriptors.
let mut kernel_fds: [i32; 2] = [0; 2];
let ret = unsafe { libc::pipe2(kernel_fds.as_mut_ptr(), flags) };
if ret < 0 {
return handle_errno(get_errno(), "pipe2_syscall");
}

// Check whether O_CLOEXEC is set.
let should_cloexec = (flags & fs_const::O_CLOEXEC) != 0;

// Get virtual fd for read end
let read_vfd = match fdtables::get_unused_virtual_fd(
cageid,
fs_const::FDKIND_KERNEL,
kernel_fds[0] as u64,
should_cloexec,
0,
) {
Ok(fd) => fd as i32,
Err(_) => {
unsafe {
libc::close(kernel_fds[0]);
libc::close(kernel_fds[1]);
}
return syscall_error(Errno::EMFILE, "pipe2_syscall", "Too many files opened");
}
};

// Get virtual fd for write end
let write_vfd = match fdtables::get_unused_virtual_fd(
cageid,
fs_const::FDKIND_KERNEL,
kernel_fds[1] as u64,
should_cloexec,
0,
) {
Ok(fd) => fd as i32,
Err(_) => {
unsafe {
libc::close(kernel_fds[0]);
libc::close(kernel_fds[1]);
}
return syscall_error(Errno::EMFILE, "pipe2_syscall", "Too many files opened");
}
};

pipefd.readfd = read_vfd;
pipefd.writefd = write_vfd;
ret
}

33 changes: 28 additions & 5 deletions src/rawposix/tests/fs_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,18 @@ fn test_open() {
cageid,
0o644 as u64,
cageid,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0,
0,
0,
);
assert!(
fd >= 0,
"open_syscall should return a valid file descriptor, got: {}",
fd
);
assert!(fd >= 0, "open_syscall should return a valid file descriptor, got: {}", fd);

// Verify that the file was created.
let metadata = fs::metadata("/tmp/test_open_syscall_file");
Expand Down Expand Up @@ -149,13 +158,27 @@ fn test_mkdir() {
cageid,
0o755 as u64,
cageid,
0, 0, 0, 0, 0, 0, 0, 0,
0,
0,
0,
0,
0,
0,
0,
0,
);
assert_eq!(
res, 0,
"mkdir_syscall should succeed and return 0, got: {}",
res
);
assert_eq!(res, 0, "mkdir_syscall should succeed and return 0, got: {}", res);

// Verify that the directory was created.
let metadata = fs::metadata("/tmp/test_mkdir_syscall_dir");
assert!(metadata.is_ok(), "Directory should exist after mkdir_syscall");
assert!(
metadata.is_ok(),
"Directory should exist after mkdir_syscall"
);

// Clean up: remove the created test directory.
let _ = fs::remove_dir("/tmp/test_mkdir_syscall_dir");
Expand Down