#![allow(clippy::unnecessary_wraps)]
use std::ffi::CString;
use std::os::fd::AsRawFd;
use std::os::unix::ffi::OsStrExt;
use io_uring::{opcode, squeue, types};
use wireshift_core::op::OpDescriptor;
use wireshift_core::{Error, Result};
use super::PreparedEntry;
use crate::file_registry::FileRegistryTable;
use crate::helpers::{build_open_flags, has_sequential_hint};
use crate::types::InflightData;
pub(crate) fn build_openat(
descriptor: &OpDescriptor,
id: u64,
_fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::OpenAt {
dir,
path,
flags: open_flags_bits,
read,
write,
create,
truncate,
} = descriptor
else {
unreachable!()
};
let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| {
Error::validation(
"openat path contains an interior NUL byte",
"pass a path without embedded NUL characters",
)
})?;
let dir_fd = dir.as_ref().map_or(libc::AT_FDCWD, AsRawFd::as_raw_fd);
let flags = build_open_flags(*open_flags_bits, *read, *write, *create, *truncate)?;
let mode = if *create { 0o600_u32 } else { 0_u32 };
let entry = opcode::OpenAt::new(types::Fd(dir_fd), path_cstr.as_ptr())
.flags(flags)
.mode(mode)
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::Path(path_cstr),
link_to_next: None,
}])
}
pub(crate) fn build_openat_direct(
descriptor: &OpDescriptor,
id: u64,
_fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::OpenAtDirect {
dir,
path,
flags: open_flags_bits,
read,
write,
create,
truncate,
slot,
} = descriptor
else {
unreachable!()
};
let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| {
Error::validation(
"openat path contains an interior NUL byte",
"pass a path without embedded NUL characters",
)
})?;
let dir_fd = dir.as_ref().map_or(libc::AT_FDCWD, AsRawFd::as_raw_fd);
let flags = build_open_flags(*open_flags_bits, *read, *write, *create, *truncate)?;
let mode = if *create { 0o600_u32 } else { 0_u32 };
let entry = opcode::OpenAt::new(types::Fd(dir_fd), path_cstr.as_ptr())
.file_index(Some(
types::DestinationSlot::try_from_slot_target(*slot).map_err(|_| {
Error::validation(
"direct descriptor slot exceeds io_uring's supported range",
"reduce the number of concurrent chained reads",
)
})?,
))
.flags(flags)
.mode(mode)
.build()
.user_data(id);
let mut entries = vec![PreparedEntry {
entry,
pinned: InflightData::Path(path_cstr),
link_to_next: None,
}];
if has_sequential_hint(*open_flags_bits) {
entries[0].link_to_next = Some(squeue::Flags::IO_LINK);
entries.push(PreparedEntry {
entry: opcode::Fadvise::new(types::Fixed(*slot), 0, libc::POSIX_FADV_SEQUENTIAL)
.offset(0)
.build()
.user_data(id),
pinned: InflightData::None,
link_to_next: None,
});
}
Ok(entries)
}
pub(crate) fn build_statx(
descriptor: &OpDescriptor,
id: u64,
_fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::Statx { path } = descriptor else {
unreachable!()
};
let path_cstr = CString::new(path.as_os_str().as_bytes()).map_err(|_| {
Error::validation(
"statx path contains an interior NUL byte",
"pass a path without embedded NUL characters",
)
})?;
let statx_buf: Box<libc::statx> = Box::new(unsafe { std::mem::zeroed() });
let statx_ptr: *mut libc::statx = Box::into_raw(statx_buf);
let entry = opcode::Statx::new(
types::Fd(libc::AT_FDCWD),
path_cstr.as_ptr(),
statx_ptr.cast::<types::statx>(),
)
.flags(libc::AT_STATX_SYNC_AS_STAT)
.mask(libc::STATX_TYPE | libc::STATX_SIZE)
.build()
.user_data(id);
let statx_buf = unsafe { Box::from_raw(statx_ptr) };
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::Statx {
path: path_cstr,
statx_buf,
},
link_to_next: None,
}])
}
pub(crate) fn build_fsync(
descriptor: &OpDescriptor,
id: u64,
fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::Fsync { file } = descriptor else {
unreachable!()
};
let entry = if let Some(slot) = fixed_files.lookup(file.as_raw_fd()) {
opcode::Fsync::new(types::Fixed(slot))
} else {
opcode::Fsync::new(types::Fd(file.as_raw_fd()))
}
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::None,
link_to_next: None,
}])
}
pub(crate) fn build_close_fixed(
descriptor: &OpDescriptor,
id: u64,
_fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::CloseFixed { slot } = descriptor else {
unreachable!()
};
let entry = opcode::Close::new(types::Fixed(*slot))
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::None,
link_to_next: None,
}])
}