Skip to content
Open
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 @@ -341,6 +341,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
170 changes: 88 additions & 82 deletions litebox_shim_linux/src/syscalls/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,117 +1012,108 @@ impl<FS: ShimFS> Task<FS> {
.ok_or(Errno::EBADF)?
.set_file_descriptor_flags(&self.global, &files, flags)
.map(|()| 0),
FcntlArg::GETFL => match desc {
Descriptor::LiteBoxRawFd(raw_fd) => Ok(files
.run_on_raw_fd(
*raw_fd,
|fd| {
Ok(self
.global
.litebox
.descriptor_table()
.with_metadata(fd, |crate::StdioStatusFlags(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)
},
)
.flatten()?
.bits()),
Descriptor::Eventfd { file, .. } => Ok(file.get_status().bits()),
Descriptor::Epoll { file, .. } => Ok(file.get_status().bits()),
Descriptor::Unix { file, .. } => Ok(file.get_status().bits()),
},
FcntlArg::GETFL => {
macro_rules! getfl_from_metadata {
($fd:expr, $MetaType:path) => {
Ok(self
.global
.litebox
.descriptor_table()
.with_metadata($fd, |$MetaType(flags)| {
*flags & OFlags::STATUS_FLAGS_MASK
})
.unwrap_or(OFlags::empty()))
};
}
match desc {
Descriptor::LiteBoxRawFd(raw_fd) => Ok(files
.run_on_raw_fd(
*raw_fd,
|fd| getfl_from_metadata!(fd, crate::StdioStatusFlags),
|fd| getfl_from_metadata!(fd, crate::syscalls::net::SocketOFlags),
|fd| getfl_from_metadata!(fd, crate::PipeStatusFlags),
)
.flatten()?
.bits()),
Descriptor::Eventfd { file, .. } => Ok(file.get_status().bits()),
Descriptor::Epoll { file, .. } => Ok(file.get_status().bits()),
Descriptor::Unix { file, .. } => Ok(file.get_status().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;
let diff = ($t.get_status() & setfl_mask) ^ flags;
if diff.intersects(OFlags::APPEND | OFlags::DIRECT | OFlags::NOATIME) {
todo!("unsupported flags");
log_unsupported!("unsupported flags");
}
$t.set_status(flags & setfl_mask, true);
$t.set_status(flags.complement() & setfl_mask, false);
};
}
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, |$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 => $no_metadata_msg,
})
};
}
match desc {
Descriptor::LiteBoxRawFd(raw_fd) => files.run_on_raw_fd(
*raw_fd,
|fd| {
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");
}
f.toggle(diff);
})
.map_err(|err| match err {
MetadataError::ClosedFd => Errno::EBADF,
MetadataError::NoSuchMetadata => {
unimplemented!("SETFL on non-stdio")
}
})
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"),
|_| {}
)
},
)??,
Descriptor::Eventfd { file, .. } => {
Expand Down Expand Up @@ -1291,6 +1282,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