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
42 changes: 42 additions & 0 deletions litebox_runner_linux_userland/tests/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,45 @@ fn test_tun_and_runner_with_iperf3() {
has_started.store(true, std::sync::atomic::Ordering::Relaxed);
runner.run();
}

#[cfg(target_arch = "x86_64")]
#[test]
fn test_tun_with_curl() {
use std::io::{Read, Write};
use std::net::TcpListener;

const RESPONSE_BODY: &str = "#!/bin/bash\necho 'Hello from litebox!'\n";

// Bind to an OS-assigned port on all interfaces.
let listener = TcpListener::bind("0.0.0.0:0").expect("Failed to bind HTTP server");
let port = listener.local_addr().unwrap().port();

let server_thread = std::thread::spawn(move || {
let (mut stream, _) = listener.accept().expect("Failed to accept connection");
let mut buf = [0u8; 4096];
let n = stream.read(&mut buf).expect("Failed to read request");
let request = String::from_utf8_lossy(&buf[..n]);
println!("Received HTTP request:\n{request}");

let response = format!(
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
RESPONSE_BODY.len(),
RESPONSE_BODY
);
stream
.write_all(response.as_bytes())
.expect("Failed to send response");
});

let curl_path = run_which("curl");
let url = format!("http://10.0.0.1:{port}/something");
let output = Runner::new(Backend::Rewriter, &curl_path, "curl_rewriter")
.args(["-sS", &url])
.tun_device_name("tun99")
.output();

server_thread.join().expect("Server thread panicked");

let output_str = String::from_utf8_lossy(&output);
assert!(output_str.contains(RESPONSE_BODY), "Unexpected curl output");
}
3 changes: 3 additions & 0 deletions litebox_shim_linux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ fn default_fs(
// Special override so that `GETFL` can return stdio-specific flags
pub(crate) struct StdioStatusFlags(litebox::fs::OFlags);

/// Status flags for pipes
pub(crate) struct PipeStatusFlags(pub litebox::fs::OFlags);

impl<FS: ShimFS> syscalls::file::FilesState<FS> {
fn initialize_stdio_in_shared_descriptors_table(&self, global: &GlobalState<FS>) {
use litebox::fs::{Mode, OFlags};
Expand Down
204 changes: 95 additions & 109 deletions litebox_shim_linux/src/syscalls/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,163 +1009,134 @@ impl<FS: ShimFS> Task<FS> {
FcntlArg::SETFD(flags) => {
set_file_descriptor_flags(desc, &self.global, &files, flags).map(|()| 0)
}
FcntlArg::GETFL => Ok(files
.run_on_raw_fd(
desc,
|fd| {
FcntlArg::GETFL => {
macro_rules! getfl_from_metadata {
($fd:expr, $MetaType:path) => {
Ok(self
.global
.litebox
.descriptor_table()
.with_metadata(fd, |crate::StdioStatusFlags(flags)| {
.with_metadata($fd, |$MetaType(flags)| {
*flags & OFlags::STATUS_FLAGS_MASK
})
.unwrap_or(OFlags::empty()))
},
|fd| {
Ok(self
.global
.litebox
.descriptor_table()
.with_metadata(fd, |crate::syscalls::net::SocketOFlags(flags)| {
*flags & OFlags::STATUS_FLAGS_MASK
})
.unwrap_or(OFlags::empty()))
},
|fd| {
let pipes = &self.global.pipes;
let flags = OFlags::from(pipes.get_flags(fd).map_err(Errno::from)?);
let dirn = match pipes.half_pipe_type(fd)? {
litebox::pipes::HalfPipeType::SenderHalf => OFlags::WRONLY,
litebox::pipes::HalfPipeType::ReceiverHalf => OFlags::RDONLY,
};
Ok(dirn | flags)
},
|fd| {
// TODO: Consider shared metadata table?
let handle = self
.global
.litebox
.descriptor_table()
.entry_handle(fd)
.ok_or(Errno::EBADF)?;
handle.with_entry(|file| Ok(file.get_status()))
},
|fd| {
// TODO: Consider shared metadata table?
let handle = self
.global
.litebox
.descriptor_table()
.entry_handle(fd)
.ok_or(Errno::EBADF)?;
handle.with_entry(|file| Ok(file.get_status()))
},
|fd| {
};
}
macro_rules! getfl_from_handle {
($fd:ident) => {{
// TODO: Consider shared metadata table?
let handle = self
.global
.litebox
.descriptor_table()
.entry_handle(fd)
.entry_handle($fd)
.ok_or(Errno::EBADF)?;
handle.with_entry(|file| Ok(file.get_status()))
},
)
.flatten()?
.bits()),
}};
}
Ok(files
.run_on_raw_fd(
desc,
|fd| getfl_from_metadata!(fd, crate::StdioStatusFlags),
|fd| getfl_from_metadata!(fd, crate::syscalls::net::SocketOFlags),
|fd| getfl_from_metadata!(fd, crate::PipeStatusFlags),
|fd| getfl_from_handle!(fd),
|fd| getfl_from_handle!(fd),
|fd| getfl_from_handle!(fd),
)
.flatten()?
.bits())
}
FcntlArg::SETFL(flags) => {
let setfl_mask = OFlags::APPEND
| OFlags::NONBLOCK
| OFlags::NDELAY
| OFlags::DIRECT
| OFlags::NOATIME;
let flags = flags & setfl_mask;
macro_rules! toggle_flags {
($t:ident) => {{
let diff = $t.get_status() ^ flags;
if diff.intersects(OFlags::APPEND | OFlags::DIRECT | OFlags::NOATIME) {
todo!("unsupported flags");
}
$t.set_status(flags & setfl_mask, true);
$t.set_status(flags.complement() & setfl_mask, false);
($fd:ident) => {{
// TODO: Consider shared metadata table?
let handle = self
.global
.litebox
.descriptor_table()
.entry_handle($fd)
.ok_or(Errno::EBADF)?;
handle.with_entry(|file| {
let diff = (file.get_status() & setfl_mask) ^ flags;
if diff.intersects(OFlags::APPEND | OFlags::DIRECT | OFlags::NOATIME) {
log_unsupported!("unsupported flags");
}
file.set_status(flags & setfl_mask, true);
file.set_status(flags.complement() & setfl_mask, false);
});
}};
}
files.run_on_raw_fd(
desc,
|fd| {
macro_rules! setfl_in_metadata {
($fd:expr, $MetaType:path, $no_metadata_msg:expr) => {
setfl_in_metadata!($fd, $MetaType, $no_metadata_msg, |diff: OFlags| {
if diff.intersects(OFlags::APPEND | OFlags::DIRECT | OFlags::NOATIME) {
log_unsupported!("unsupported flags");
}
})
};
($fd:expr, $MetaType:path, $no_metadata_msg:expr, $check_diff:expr) => {
self.global
.litebox
.descriptor_table_mut()
.with_metadata_mut(fd, |crate::StdioStatusFlags(f)| {
let diff = *f ^ flags;
if diff
.intersects(OFlags::APPEND | OFlags::DIRECT | OFlags::NOATIME)
{
todo!("unsupported flags");
}
.with_metadata_mut($fd, |$MetaType(f)| {
let diff = (*f & setfl_mask) ^ flags;
$check_diff(diff);
f.toggle(diff);
})
.map_err(|err| match err {
MetadataError::ClosedFd => Errno::EBADF,
MetadataError::NoSuchMetadata => {
unimplemented!("SETFL on non-stdio")
}
MetadataError::NoSuchMetadata => $no_metadata_msg,
})
};
}
files.run_on_raw_fd(
desc,
|fd| {
setfl_in_metadata!(
fd,
crate::StdioStatusFlags,
unimplemented!("SETFL on non-stdio")
)
},
|fd| {
self.global
.litebox
.descriptor_table_mut()
.with_metadata_mut(fd, |crate::syscalls::net::SocketOFlags(f)| {
let diff = *f ^ flags;
if diff
.intersects(OFlags::APPEND | OFlags::DIRECT | OFlags::NOATIME)
{
todo!("unsupported flags");
}
f.toggle(diff);
})
.map_err(|err| match err {
MetadataError::ClosedFd => Errno::EBADF,
MetadataError::NoSuchMetadata => {
unreachable!("all sockets have SocketOFlags when created")
}
})
setfl_in_metadata!(
fd,
crate::syscalls::net::SocketOFlags,
unreachable!("all sockets have SocketOFlags when created")
)
},
|fd| {
if flags.intersects(OFlags::NONBLOCK.complement()) {
todo!("unsupported flags for pipes")
}
// Update the actual pipe non-blocking behavior
self.global
.pipes
.update_flags(
fd,
litebox::pipes::Flags::NON_BLOCKING,
flags.intersects(OFlags::NONBLOCK),
)
.map_err(Errno::from)
.map_err(Errno::from)?;
// Record all status flags in metadata for F_GETFL
setfl_in_metadata!(
fd,
crate::PipeStatusFlags,
unreachable!("all pipes have PipeStatusFlags when created"),
|_| {}
)
},
|fd| {
// TODO: Consider shared metadata table?
let handle = self
.global
.litebox
.descriptor_table()
.entry_handle(fd)
.ok_or(Errno::EBADF)?;
handle.with_entry(|file| toggle_flags!(file));
toggle_flags!(fd);
Ok(())
},
|_fd| todo!("epoll"),
|fd| {
// TODO: Consider shared metadata table?
let handle = self
.global
.litebox
.descriptor_table()
.entry_handle(fd)
.ok_or(Errno::EBADF)?;
handle.with_entry(|file| toggle_flags!(file));
toggle_flags!(fd);
Ok(())
},
)??;
Expand Down Expand Up @@ -1326,6 +1297,21 @@ impl<FS: ShimFS> Task<FS> {
core::num::NonZero::new(4096),
);

{
let initial_status = OFlags::from(pipe_flags);
let mut dt = self.global.litebox.descriptor_table_mut();
let old = dt.set_entry_metadata(
&writer,
crate::PipeStatusFlags(initial_status | OFlags::WRONLY),
);
assert!(old.is_none());
let old = dt.set_entry_metadata(
&reader,
crate::PipeStatusFlags(initial_status | OFlags::RDONLY),
);
assert!(old.is_none());
}

if cloexec {
let mut dt = self.global.litebox.descriptor_table_mut();
let None = dt.set_fd_metadata(&writer, FileDescriptorFlags::FD_CLOEXEC) else {
Expand Down
4 changes: 2 additions & 2 deletions litebox_shim_linux/src/syscalls/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ fn test_fcntl() {
.unwrap();
assert_eq!(task.sys_fcntl(fd, FcntlArg::GETFD).unwrap(), 0);

task.sys_fcntl(fd, FcntlArg::SETFL(OFlags::empty()))
.unwrap();
// OFlags::RDWR should be ignored
task.sys_fcntl(fd, FcntlArg::SETFL(OFlags::RDWR)).unwrap();
assert_eq!(task.sys_fcntl(fd, FcntlArg::GETFL).unwrap(), flags2.bits());
};

Expand Down
Loading